Run prettier when using eslint to make code consistantly formatted.
This commit is contained in:
parent
2d0c1d7689
commit
9c50ca2432
|
@ -1,14 +1,12 @@
|
|||
{
|
||||
"plugins": [
|
||||
"sort-imports-es6-autofix",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"plugins": ["sort-imports-es6-autofix", "@typescript-eslint", "prettier"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking"
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"prettier"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
|
@ -23,9 +21,7 @@
|
|||
"BUILD_STRING": "writable"
|
||||
},
|
||||
"rules": {
|
||||
"no-unused-vars": [
|
||||
"off"
|
||||
],
|
||||
"no-unused-vars": ["off"],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
|
@ -38,15 +34,10 @@
|
|||
"allow": ["arrowFunctions"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/ban-ts-comment": [
|
||||
"off"
|
||||
],
|
||||
"sort-imports-es6-autofix/sort-imports-es6": [
|
||||
2
|
||||
],
|
||||
"@typescript-eslint/no-explicit-any": [
|
||||
2
|
||||
]
|
||||
"@typescript-eslint/ban-ts-comment": ["off"],
|
||||
"sort-imports-es6-autofix/sort-imports-es6": [2],
|
||||
"@typescript-eslint/no-explicit-any": [2],
|
||||
"prettier/prettier": 2
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
|
@ -57,10 +48,7 @@
|
|||
"settings": {
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
"extensions": [
|
||||
".js",
|
||||
".ts"
|
||||
]
|
||||
"extensions": [".js", ".ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
.prettierrc
Normal file
6
.prettierrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": false,
|
||||
"printWidth": 100
|
||||
}
|
|
@ -10,7 +10,9 @@
|
|||
"css-loader": "^5.2.4",
|
||||
"date-fns": "^2.21.3",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"git-revision-webpack-plugin": "^5.0.0",
|
||||
|
@ -18,6 +20,7 @@
|
|||
"i18next": "^20.2.2",
|
||||
"mini-css-extract-plugin": "^1.6.0",
|
||||
"node-sass": "^5.0.0",
|
||||
"prettier": "^2.3.0",
|
||||
"prismjs": "^1.23.0",
|
||||
"qr-scanner": "^1.2.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { Page } from "./types/Page";
|
||||
import { allPages } from "./allPages"
|
||||
import {
|
||||
getKeyByObjectPropertyValue,
|
||||
} from "./utils";
|
||||
import { allPages } from "./allPages";
|
||||
import { getKeyByObjectPropertyValue } from "./utils";
|
||||
|
||||
export class PageState {
|
||||
constructor() {
|
||||
|
@ -19,43 +17,42 @@ export class PageState {
|
|||
// the clunkyness of this approach, but for now, this works.
|
||||
|
||||
get apiURL(): string | null {
|
||||
const apiurl = localStorage.getItem('apiURL') || "";
|
||||
const apiurl = localStorage.getItem("apiURL") || "";
|
||||
return apiurl.length > 0 ? apiurl : null;
|
||||
}
|
||||
set apiURL(value: string) {
|
||||
localStorage.setItem('apiURL', value);
|
||||
localStorage.setItem("apiURL", value);
|
||||
}
|
||||
|
||||
get token(): string | null {
|
||||
const tok = localStorage.getItem('token') || "";
|
||||
const tok = localStorage.getItem("token") || "";
|
||||
return tok.length > 0 ? tok : null;
|
||||
}
|
||||
set token(value: string) {
|
||||
localStorage.setItem('token', value);
|
||||
localStorage.setItem("token", value);
|
||||
}
|
||||
|
||||
get pageDirection(): string {
|
||||
return localStorage.getItem('pageDirection') || "ltr";
|
||||
return localStorage.getItem("pageDirection") || "ltr";
|
||||
}
|
||||
set pageDirection(value: string) {
|
||||
localStorage.setItem('pageDirection', value);
|
||||
localStorage.setItem("pageDirection", value);
|
||||
}
|
||||
|
||||
get language(): string {
|
||||
return localStorage.getItem('language') || "";
|
||||
return localStorage.getItem("language") || "";
|
||||
}
|
||||
set language(value: string) {
|
||||
localStorage.setItem('language', value);
|
||||
localStorage.setItem("language", value);
|
||||
}
|
||||
|
||||
get currentBaseMount(): string {
|
||||
return localStorage.getItem('currentBaseMount') || "";
|
||||
return localStorage.getItem("currentBaseMount") || "";
|
||||
}
|
||||
set currentBaseMount(value: string) {
|
||||
localStorage.setItem('currentBaseMount', value);
|
||||
localStorage.setItem("currentBaseMount", value);
|
||||
}
|
||||
|
||||
|
||||
// Since this is a array we can't act directly on it so we need
|
||||
// functions to do the same modifications.
|
||||
// See the note at the start o
|
||||
|
@ -71,47 +68,47 @@ export class PageState {
|
|||
}
|
||||
|
||||
get currentSecretPath(): string[] {
|
||||
return JSON.parse(localStorage.getItem('currentSecretPath') || "[]") as string[];
|
||||
return JSON.parse(localStorage.getItem("currentSecretPath") || "[]") as string[];
|
||||
}
|
||||
set currentSecretPath(value: string[]) {
|
||||
localStorage.setItem('currentSecretPath', JSON.stringify(value));
|
||||
localStorage.setItem("currentSecretPath", JSON.stringify(value));
|
||||
}
|
||||
|
||||
get currentSecretVersion(): string | null {
|
||||
const result = localStorage.getItem('currentSecretVersion')
|
||||
const result = localStorage.getItem("currentSecretVersion");
|
||||
return result != "null" ? result || null : null;
|
||||
}
|
||||
set currentSecretVersion(value: string) {
|
||||
localStorage.setItem('currentSecretVersion', String(value));
|
||||
localStorage.setItem("currentSecretVersion", String(value));
|
||||
}
|
||||
|
||||
get currentSecret(): string {
|
||||
return localStorage.getItem('currentSecret') || "";
|
||||
return localStorage.getItem("currentSecret") || "";
|
||||
}
|
||||
set currentSecret(value: string) {
|
||||
localStorage.setItem('currentSecret', value);
|
||||
localStorage.setItem("currentSecret", value);
|
||||
}
|
||||
|
||||
get currentMountType(): string {
|
||||
return localStorage.getItem('currentMountType') || "";
|
||||
return localStorage.getItem("currentMountType") || "";
|
||||
}
|
||||
set currentMountType(value: string) {
|
||||
localStorage.setItem('currentMountType', value);
|
||||
localStorage.setItem("currentMountType", value);
|
||||
}
|
||||
get currentPageString(): string {
|
||||
const key = getKeyByObjectPropertyValue(allPages, this.currentPage);
|
||||
return key;
|
||||
}
|
||||
get currentPage(): Page | string {
|
||||
const curPage = localStorage.getItem('currentPage') || "HOME";
|
||||
const curPage = localStorage.getItem("currentPage") || "HOME";
|
||||
return allPages[curPage];
|
||||
}
|
||||
set currentPage(value: Page | string) {
|
||||
if (typeof value == 'object') {
|
||||
if (typeof value == "object") {
|
||||
const key = getKeyByObjectPropertyValue(allPages, value);
|
||||
localStorage.setItem('currentPage', key);
|
||||
localStorage.setItem("currentPage", key);
|
||||
} else {
|
||||
localStorage.setItem('currentPage', value);
|
||||
localStorage.setItem("currentPage", value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,8 +21,8 @@ import { TransitViewSecretPage } from "./pages/Transit/TransitViewSecret";
|
|||
import { UnsealPage } from "./pages/Unseal";
|
||||
|
||||
type pagesList = {
|
||||
[key: string]: Page
|
||||
}
|
||||
[key: string]: Page;
|
||||
};
|
||||
|
||||
export const allPages: pagesList = {
|
||||
HOME: new HomePage(),
|
||||
|
|
|
@ -3,7 +3,7 @@ import { pageState } from "../globalPageState";
|
|||
export function getHeaders(): Record<string, string> {
|
||||
return {
|
||||
"X-Vault-Token": pageState.token,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const appendAPIURL = (url: string): string => pageState.apiURL + url;
|
|
@ -2,15 +2,18 @@ import { appendAPIURL } from "../apiUtils";
|
|||
|
||||
export async function usernameLogin(username: string, password: string): Promise<string> {
|
||||
const request = new Request(appendAPIURL(`/v1/auth/userpass/login/${username}`), {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ "username": username, "password": password })
|
||||
body: JSON.stringify({ username: username, password: password }),
|
||||
});
|
||||
|
||||
const resp = await fetch(request);
|
||||
const data = await resp.json() as { auth?: { client_token: string }; errors?: string[] };
|
||||
const data = (await resp.json()) as {
|
||||
auth?: { client_token: string };
|
||||
errors?: string[];
|
||||
};
|
||||
if ("auth" in data) {
|
||||
return data.auth.client_token;
|
||||
} else if ("errors" in data) {
|
||||
|
|
|
@ -6,14 +6,14 @@ export async function createOrUpdateSecret(
|
|||
mountType: string,
|
||||
secretPath: string[],
|
||||
name: string,
|
||||
data: Record<string, unknown>
|
||||
data: Record<string, unknown>,
|
||||
): Promise<void> {
|
||||
let secretURL = "";
|
||||
let APIData = {};
|
||||
|
||||
if (mountType == "kv-v2") {
|
||||
secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`;
|
||||
APIData = { "data": data };
|
||||
APIData = { data: data };
|
||||
} else {
|
||||
secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`;
|
||||
APIData = data;
|
||||
|
@ -23,14 +23,14 @@ export async function createOrUpdateSecret(
|
|||
const request = new Request(appendAPIURL(secretURL), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
...getHeaders(),
|
||||
},
|
||||
body: JSON.stringify(APIData, null, 0)
|
||||
body: JSON.stringify(APIData, null, 0),
|
||||
});
|
||||
const response = await fetch(request);
|
||||
if (!response.ok) {
|
||||
const json = await response.json() as {errors: string[]};
|
||||
const json = (await response.json()) as { errors: string[] };
|
||||
throw new Error(json.errors[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ export async function deleteSecret(
|
|||
mountType: string,
|
||||
secretPath: string[],
|
||||
name: string,
|
||||
version: string | null = null
|
||||
version: string | null = null,
|
||||
): Promise<void> {
|
||||
let secretURL = "";
|
||||
|
||||
|
@ -19,9 +19,9 @@ export async function deleteSecret(
|
|||
method: "POST",
|
||||
headers: {
|
||||
...getHeaders(),
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: version != null ? JSON.stringify({ "versions": [version] }) : "{}"
|
||||
body: version != null ? JSON.stringify({ versions: [version] }) : "{}",
|
||||
});
|
||||
} else {
|
||||
if (mountType == "kv-v2") {
|
||||
|
@ -37,7 +37,7 @@ export async function deleteSecret(
|
|||
}
|
||||
const response = await fetch(request);
|
||||
if (!response.ok) {
|
||||
const json = await response.json() as {errors: string[]};
|
||||
const json = (await response.json()) as { errors: string[] };
|
||||
throw new Error(json.errors[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,12 @@ export async function getSecret(
|
|||
mountType: string,
|
||||
secretPath: string[],
|
||||
name: string,
|
||||
version: string|null = null
|
||||
version: string | null = null,
|
||||
): Promise<Record<string, unknown>> {
|
||||
let secretURL = "";
|
||||
if (mountType == "kv-v2") {
|
||||
secretURL = `/v1/${baseMount}/data/${secretPath.join("")}/${name}`;
|
||||
if (version != null)
|
||||
secretURL += `?version=${version}`;
|
||||
if (version != null) secretURL += `?version=${version}`;
|
||||
} else {
|
||||
secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
|
||||
}
|
||||
|
@ -20,10 +19,10 @@ export async function getSecret(
|
|||
});
|
||||
|
||||
const resp = await fetch(request);
|
||||
const data = await resp.json() as unknown;
|
||||
const data = (await resp.json()) as unknown;
|
||||
if (mountType == "kv-v2") {
|
||||
return (data as {data: {data: Record<string, unknown>}}).data.data;
|
||||
return (data as { data: { data: Record<string, unknown> } }).data.data;
|
||||
} else {
|
||||
return (data as {data: Record<string, unknown>}).data;
|
||||
return (data as { data: Record<string, unknown> }).data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||
|
||||
type SecretMetadataType = {
|
||||
versions: Record<string, unknown>
|
||||
}
|
||||
versions: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export async function getSecretMetadata(
|
||||
baseMount: string,
|
||||
secretPath: string[],
|
||||
name: string
|
||||
name: string,
|
||||
): Promise<SecretMetadataType> {
|
||||
const request = new Request(appendAPIURL(`/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`), {
|
||||
const request = new Request(
|
||||
appendAPIURL(`/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`),
|
||||
{
|
||||
headers: getHeaders(),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const resp = await fetch(request);
|
||||
const data = await resp.json() as {data: SecretMetadataType};
|
||||
const data = (await resp.json()) as { data: SecretMetadataType };
|
||||
return data.data;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { appendAPIURL, getHeaders } from "../apiUtils";
|
|||
export async function getSecrets(
|
||||
baseMount: string,
|
||||
mountType: string,
|
||||
secretPath: string[]
|
||||
secretPath: string[],
|
||||
): Promise<string[]> {
|
||||
let secretURL = "";
|
||||
if (mountType == "kv-v2") {
|
||||
|
@ -20,6 +20,6 @@ export async function getSecrets(
|
|||
if (resp.status == 404) {
|
||||
throw DoesNotExistError;
|
||||
}
|
||||
const data = await resp.json() as { data: { keys: string[] } };
|
||||
const data = (await resp.json()) as { data: { keys: string[] } };
|
||||
return data.data.keys;
|
||||
}
|
||||
|
|
|
@ -6,16 +6,12 @@ export async function undeleteSecret(
|
|||
baseMount: string,
|
||||
secretPath: string[],
|
||||
name: string,
|
||||
version: string|null = null
|
||||
version: string | null = null,
|
||||
): Promise<void> {
|
||||
let secretURL = `/v1/${baseMount}/undelete/${secretPath.join("/")}/${name}`;
|
||||
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
|
||||
if (version == null) {
|
||||
const meta = await getSecretMetadata(
|
||||
baseMount,
|
||||
secretPath,
|
||||
name
|
||||
);
|
||||
const meta = await getSecretMetadata(baseMount, secretPath, name);
|
||||
const versions = getObjectKeys(meta.versions);
|
||||
version = String(versions[versions.length - 1]);
|
||||
}
|
||||
|
@ -24,13 +20,13 @@ export async function undeleteSecret(
|
|||
method: "POST",
|
||||
headers: {
|
||||
...getHeaders(),
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ "versions": [version] })
|
||||
body: JSON.stringify({ versions: [version] }),
|
||||
});
|
||||
const response = await fetch(request);
|
||||
if (!response.ok) {
|
||||
const json = await response.json() as {errors: string[]};
|
||||
const json = (await response.json()) as { errors: string[] };
|
||||
throw new Error(json.errors[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||
import { removeDoubleSlash } from "../../utils";
|
||||
|
||||
|
||||
export async function getCapabilitiesPath(path: string): Promise<string[]> {
|
||||
const request = new Request(appendAPIURL("/v1/sys/capabilities-self"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
...getHeaders(),
|
||||
},
|
||||
body: JSON.stringify(
|
||||
{
|
||||
"paths": [removeDoubleSlash(path)]
|
||||
}
|
||||
)
|
||||
body: JSON.stringify({
|
||||
paths: [removeDoubleSlash(path)],
|
||||
}),
|
||||
});
|
||||
const response = await fetch(request);
|
||||
const data = await response.json() as {capabilities: string[]};
|
||||
const data = (await response.json()) as { capabilities: string[] };
|
||||
return data.capabilities;
|
||||
}
|
||||
|
||||
export async function getCapabilities(
|
||||
baseMount: string,
|
||||
secretPath: string[],
|
||||
name: string
|
||||
name: string,
|
||||
): Promise<string[]> {
|
||||
return await getCapabilitiesPath(removeDoubleSlash(baseMount + secretPath.join("/") + "/" + name));
|
||||
return await getCapabilitiesPath(
|
||||
removeDoubleSlash(baseMount + secretPath.join("/") + "/" + name),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||
|
||||
export type MountType = {
|
||||
type: string
|
||||
type: string;
|
||||
options: {
|
||||
version: string
|
||||
}
|
||||
}
|
||||
version: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type MountsType = {
|
||||
[key: string]: MountType;
|
||||
}
|
||||
};
|
||||
|
||||
export async function getMounts(): Promise<MountsType> {
|
||||
const request = new Request(appendAPIURL("/v1/sys/internal/ui/mounts"), {
|
||||
headers: getHeaders(),
|
||||
});
|
||||
const resp = await fetch(request);
|
||||
const data = await resp.json() as {data: {secret: MountsType}};
|
||||
const data = (await resp.json()) as { data: { secret: MountsType } };
|
||||
return data.data.secret;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@ export type SealStatusType = {
|
|||
progress: number;
|
||||
t: number;
|
||||
sealed: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export async function getSealStatus(): Promise<SealStatusType> {
|
||||
const request = new Request(appendAPIURL("/v1/sys/seal-status"));
|
||||
const resp = await fetch(request)
|
||||
const data = await resp.json() as SealStatusType;
|
||||
const resp = await fetch(request);
|
||||
const data = (await resp.json()) as SealStatusType;
|
||||
return data;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ export async function lookupSelf(): Promise<TokenInfo> {
|
|||
headers: getHeaders(),
|
||||
});
|
||||
const resp = await fetch(request);
|
||||
const data = await resp.json() as { data?: TokenInfo; errors?: string[] };
|
||||
const data = (await resp.json()) as { data?: TokenInfo; errors?: string[] };
|
||||
if ("data" in data) {
|
||||
return data.data;
|
||||
} else if ("errors" in data) {
|
||||
|
|
|
@ -2,15 +2,15 @@ import { appendAPIURL, getHeaders } from "../apiUtils";
|
|||
|
||||
export async function renewSelf(): Promise<void> {
|
||||
const request = new Request(appendAPIURL("/v1/auth/token/renew-self"), {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
...getHeaders(),
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
const resp = await fetch(request)
|
||||
const data = await resp.json() as { errors?: string[] };
|
||||
const resp = await fetch(request);
|
||||
const data = (await resp.json()) as { errors?: string[] };
|
||||
if ("errors" in data) {
|
||||
throw new Error(data.errors[0]);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||
|
||||
|
||||
export async function sealVault(): Promise<void> {
|
||||
const request = new Request(appendAPIURL("/v1/sys/seal"), {
|
||||
method: 'PUT',
|
||||
method: "PUT",
|
||||
headers: getHeaders(),
|
||||
});
|
||||
const resp = await fetch(request)
|
||||
const data = await resp.json() as { errors?: string[] };
|
||||
const resp = await fetch(request);
|
||||
const data = (await resp.json()) as { errors?: string[] };
|
||||
if ("errors" in data) {
|
||||
throw new Error(data.errors[0]);
|
||||
}
|
||||
|
|
|
@ -4,15 +4,15 @@ export async function submitUnsealKey(key: string): Promise<void> {
|
|||
const request = new Request(appendAPIURL("/v1/sys/unseal"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"key": key
|
||||
})
|
||||
key: key,
|
||||
}),
|
||||
});
|
||||
const resp = await fetch(request)
|
||||
const resp = await fetch(request);
|
||||
if (!resp.ok) {
|
||||
const data = await resp.json() as { errors?: string[] };
|
||||
const data = (await resp.json()) as { errors?: string[] };
|
||||
if ("errors" in data) {
|
||||
throw new Error(data.errors[0]);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||
import { removeDoubleSlash } from "../../utils";
|
||||
|
||||
export async function addNewTOTP(baseMount: string, parms: {name: string}): Promise<void> {
|
||||
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)), {
|
||||
method: 'POST',
|
||||
export async function addNewTOTP(baseMount: string, parms: { name: string }): Promise<void> {
|
||||
const request = new Request(
|
||||
appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
...getHeaders(),
|
||||
},
|
||||
body: JSON.stringify(parms)
|
||||
});
|
||||
const resp = await fetch(request)
|
||||
body: JSON.stringify(parms),
|
||||
},
|
||||
);
|
||||
const resp = await fetch(request);
|
||||
if (!resp.ok) {
|
||||
const data = await resp.json() as { errors?: string[] };
|
||||
const data = (await resp.json()) as { errors?: string[] };
|
||||
if ("errors" in data) {
|
||||
throw new Error(data.errors[0]);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||
|
||||
export async function getTOTPCode(baseMount: string, name: string): Promise<string> {
|
||||
const request =
|
||||
new Request(appendAPIURL(`/v1/${baseMount}/code/${name}`), {
|
||||
const request = new Request(appendAPIURL(`/v1/${baseMount}/code/${name}`), {
|
||||
headers: getHeaders(),
|
||||
});
|
||||
const resp = await fetch(request)
|
||||
const data = await resp.json() as {data: {code: string}};
|
||||
const resp = await fetch(request);
|
||||
const data = (await resp.json()) as { data: { code: string } };
|
||||
return data.data.code;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ export async function getTOTPKeys(baseMount: string): Promise<string[]> {
|
|||
if (resp.status == 404) {
|
||||
throw DoesNotExistError;
|
||||
}
|
||||
const data = await resp.json() as {data: {keys: string[] }};
|
||||
const data = (await resp.json()) as { data: { keys: string[] } };
|
||||
return data.data.keys;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@ export async function getTransitKey(baseMount: string, name: string): Promise<Tr
|
|||
if (resp.status == 404) {
|
||||
throw DoesNotExistError;
|
||||
}
|
||||
const data = await resp.json() as { data: TransitKeyType };
|
||||
const data = (await resp.json()) as { data: TransitKeyType };
|
||||
return data.data;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,6 @@ export async function getTransitKeys(baseMount: string): Promise<string[]> {
|
|||
if (resp.status == 404) {
|
||||
throw DoesNotExistError;
|
||||
}
|
||||
const data = await resp.json() as { data: string[] };
|
||||
const data = (await resp.json()) as { data: string[] };
|
||||
return data.data;
|
||||
}
|
||||
|
|
|
@ -3,28 +3,31 @@ import { removeDoubleSlash } from "../../utils";
|
|||
|
||||
type DecryptionResult = {
|
||||
plaintext: string;
|
||||
}
|
||||
};
|
||||
|
||||
type DecryptionPayload = {
|
||||
ciphertext: string;
|
||||
}
|
||||
};
|
||||
|
||||
export async function transitDecrypt(
|
||||
baseMount: string,
|
||||
name: string,
|
||||
payload: DecryptionPayload
|
||||
payload: DecryptionPayload,
|
||||
): Promise<DecryptionResult> {
|
||||
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/decrypt/${name}`)), {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
...getHeaders(),
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const response = await fetch(request);
|
||||
const data = await response.json() as { errors?: string[]; data?: DecryptionResult; };
|
||||
const data = (await response.json()) as {
|
||||
errors?: string[];
|
||||
data?: DecryptionResult;
|
||||
};
|
||||
if (!response.ok) {
|
||||
throw new Error(data.errors[0]);
|
||||
} else {
|
||||
|
|
|
@ -3,28 +3,31 @@ import { removeDoubleSlash } from "../../utils";
|
|||
|
||||
type EncryptionResult = {
|
||||
ciphertext: string;
|
||||
}
|
||||
};
|
||||
|
||||
type EncryptionPayload = {
|
||||
plaintext: string;
|
||||
}
|
||||
};
|
||||
|
||||
export async function transitEncrypt(
|
||||
baseMount: string,
|
||||
name: string,
|
||||
payload: EncryptionPayload
|
||||
payload: EncryptionPayload,
|
||||
): Promise<EncryptionResult> {
|
||||
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/encrypt/${name}`)), {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
...getHeaders(),
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const response = await fetch(request);
|
||||
const data = await response.json() as { errors?: string[]; data?: EncryptionResult; };
|
||||
const data = (await response.json()) as {
|
||||
errors?: string[];
|
||||
data?: EncryptionResult;
|
||||
};
|
||||
if (!response.ok) {
|
||||
throw new Error(data.errors[0]);
|
||||
} else {
|
||||
|
|
|
@ -3,29 +3,32 @@ import { removeDoubleSlash } from "../../utils";
|
|||
|
||||
type RewrapResult = {
|
||||
ciphertext: string;
|
||||
}
|
||||
};
|
||||
|
||||
type RewrapPayload = {
|
||||
ciphertext: string;
|
||||
key_version?: number;
|
||||
}
|
||||
};
|
||||
|
||||
export async function transitRewrap(
|
||||
baseMount: string,
|
||||
name: string,
|
||||
payload: RewrapPayload
|
||||
payload: RewrapPayload,
|
||||
): Promise<RewrapResult> {
|
||||
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/rewrap/${name}`)), {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
...getHeaders(),
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const response = await fetch(request);
|
||||
const data = await response.json() as { errors?: string[]; data?: RewrapResult; };
|
||||
const data = (await response.json()) as {
|
||||
errors?: string[];
|
||||
data?: RewrapResult;
|
||||
};
|
||||
if (!response.ok) {
|
||||
throw new Error(data.errors[0]);
|
||||
} else {
|
||||
|
|
|
@ -16,4 +16,4 @@ export type TokenInfo = {
|
|||
policies: string[];
|
||||
renewable: boolean;
|
||||
ttl: number;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ export type TransitKeyBaseType = {
|
|||
exportable: boolean;
|
||||
allow_plaintext_backup: boolean;
|
||||
type: keyof typeof TransitKeyTypes;
|
||||
}
|
||||
};
|
||||
|
||||
// Type returned when calling getTransitKey
|
||||
export type TransitKeyType = TransitKeyBaseType & {
|
||||
|
@ -32,4 +32,4 @@ export type TransitKeyType = TransitKeyBaseType & {
|
|||
supports_decryption: boolean;
|
||||
supports_derivation: boolean;
|
||||
supports_signing: boolean;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ export interface CopyableInputBoxType extends HTMLElement {
|
|||
}
|
||||
|
||||
export function CopyableInputBox(text: string, copyable = true): CopyableInputBoxType {
|
||||
const inputBoxDiv = (makeElement({ tag: "div" }) as CopyableInputBoxType);
|
||||
const inputBoxDiv = makeElement({ tag: "div" }) as CopyableInputBoxType;
|
||||
let inputBoxCopyButton: HTMLElement = null;
|
||||
if (copyable) {
|
||||
inputBoxCopyButton = makeElement({
|
||||
|
@ -17,26 +17,23 @@ export function CopyableInputBox(text: string, copyable = true): CopyableInputBo
|
|||
class: "uk-form-icon",
|
||||
attributes: {
|
||||
"uk-icon": "icon: copy",
|
||||
"role": "img",
|
||||
"aria-label": i18next.t("copy_input_box_copy_icon_text")
|
||||
role: "img",
|
||||
"aria-label": i18next.t("copy_input_box_copy_icon_text"),
|
||||
},
|
||||
thenRun: (e) => {
|
||||
const clipboard = new ClipboardJS(e);
|
||||
addClipboardNotifications(clipboard, 600);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const inputBoxInput = makeElement({
|
||||
tag: "input",
|
||||
class: ["uk-input", "uk-input-copyable"],
|
||||
attributes: { "readonly": "true", "type": "text" },
|
||||
attributes: { readonly: "true", type: "text" },
|
||||
}) as HTMLInputElement;
|
||||
|
||||
const inputBoxInner = MarginInline([
|
||||
inputBoxCopyButton,
|
||||
inputBoxInput
|
||||
]);
|
||||
const inputBoxInner = MarginInline([inputBoxCopyButton, inputBoxInput]);
|
||||
inputBoxDiv.appendChild(inputBoxInner);
|
||||
|
||||
inputBoxDiv.setText = function (text) {
|
||||
|
@ -47,6 +44,5 @@ export function CopyableInputBox(text: string, copyable = true): CopyableInputBo
|
|||
};
|
||||
inputBoxDiv.setText(text);
|
||||
|
||||
|
||||
return inputBoxDiv;
|
||||
}
|
|
@ -1,24 +1,24 @@
|
|||
import { addClipboardNotifications } from "../pageUtils";
|
||||
import { makeElement } from "../htmlUtils";
|
||||
import ClipboardJS from "clipboard";
|
||||
import FileSaver from 'file-saver';
|
||||
import UIkit from 'uikit';
|
||||
import i18next from 'i18next';
|
||||
import FileSaver from "file-saver";
|
||||
import UIkit from "uikit";
|
||||
import i18next from "i18next";
|
||||
|
||||
type FileSaverType = {
|
||||
saveAs: (blob: Blob, name: string) => void;
|
||||
}
|
||||
};
|
||||
|
||||
type ModalType = HTMLElement & {
|
||||
show: () => void;
|
||||
}
|
||||
};
|
||||
|
||||
export function CopyableModal(name: string, contentString: string): ModalType {
|
||||
const modal = makeElement({
|
||||
tag: "div",
|
||||
class: "modal-sections",
|
||||
attributes: {
|
||||
"uk-modal": ""
|
||||
"uk-modal": "",
|
||||
},
|
||||
children: makeElement({
|
||||
tag: "div",
|
||||
|
@ -29,8 +29,8 @@ export function CopyableModal(name: string, contentString: string): ModalType {
|
|||
class: "uk-modal-close-default",
|
||||
attributes: {
|
||||
"uk-close": "",
|
||||
type: "button"
|
||||
}
|
||||
type: "button",
|
||||
},
|
||||
}),
|
||||
makeElement({
|
||||
tag: "div",
|
||||
|
@ -38,8 +38,8 @@ export function CopyableModal(name: string, contentString: string): ModalType {
|
|||
children: makeElement({
|
||||
tag: "h2",
|
||||
class: "uk-modal-title",
|
||||
text: name
|
||||
})
|
||||
text: name,
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "div",
|
||||
|
@ -47,8 +47,8 @@ export function CopyableModal(name: string, contentString: string): ModalType {
|
|||
children: makeElement({
|
||||
tag: "pre",
|
||||
class: "wrap-pre",
|
||||
text: contentString
|
||||
})
|
||||
text: contentString,
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "div",
|
||||
|
@ -59,41 +59,42 @@ export function CopyableModal(name: string, contentString: string): ModalType {
|
|||
class: ["uk-button", "uk-button-primary"],
|
||||
attributes: {
|
||||
type: "button",
|
||||
"data-clipboard-text": contentString
|
||||
"data-clipboard-text": contentString,
|
||||
},
|
||||
text: i18next.t("copy_modal_download_btn"),
|
||||
onclick: () => {
|
||||
const blob = new Blob([contentString], { type: "text/plain;charset=utf-8" });
|
||||
const blob = new Blob([contentString], {
|
||||
type: "text/plain;charset=utf-8",
|
||||
});
|
||||
(FileSaver as FileSaverType).saveAs(blob, "result.txt");
|
||||
}
|
||||
},
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
attributes: {
|
||||
type: "button",
|
||||
"data-clipboard-text": contentString
|
||||
"data-clipboard-text": contentString,
|
||||
},
|
||||
text: i18next.t("copy_modal_copy_btn"),
|
||||
thenRun: (e) => {
|
||||
const clipboard = new ClipboardJS(e);
|
||||
addClipboardNotifications(clipboard);
|
||||
}
|
||||
},
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-secondary", "uk-modal-close"],
|
||||
attributes: { type: "button" },
|
||||
text: i18next.t("copy_modal_close_btn")
|
||||
text: i18next.t("copy_modal_close_btn"),
|
||||
}),
|
||||
|
||||
]
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]
|
||||
})
|
||||
}) as ModalType;
|
||||
modal.show = () => {
|
||||
UIkit.modal(modal).show();
|
||||
}
|
||||
};
|
||||
return modal;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ export function FileUploadInput(name: string): Element {
|
|||
tag: "input",
|
||||
attributes: {
|
||||
name: name,
|
||||
type: "file"
|
||||
}
|
||||
type: "file",
|
||||
},
|
||||
});
|
||||
|
||||
const selectInput = makeElement({
|
||||
|
@ -15,8 +15,8 @@ export function FileUploadInput(name: string): Element {
|
|||
class: ["uk-input", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
type: "text",
|
||||
placeholder: i18next.t("file_upload_input_btn")
|
||||
}
|
||||
placeholder: i18next.t("file_upload_input_btn"),
|
||||
},
|
||||
});
|
||||
|
||||
const fileIcon = makeElement({
|
||||
|
@ -24,19 +24,15 @@ export function FileUploadInput(name: string): Element {
|
|||
class: "uk-form-icon",
|
||||
attributes: {
|
||||
"uk-icon": "icon: upload",
|
||||
"role": "img"
|
||||
role: "img",
|
||||
},
|
||||
});
|
||||
|
||||
return makeElement({
|
||||
tag: "div",
|
||||
attributes: {
|
||||
"uk-form-custom": "target: true"
|
||||
"uk-form-custom": "target: true",
|
||||
},
|
||||
children: [
|
||||
fileIcon,
|
||||
fileInput,
|
||||
selectInput
|
||||
]
|
||||
children: [fileIcon, fileInput, selectInput],
|
||||
});
|
||||
}
|
|
@ -3,6 +3,6 @@ import { makeElement } from "../htmlUtils";
|
|||
export function ListItem(children: Element[] | Element): HTMLElement {
|
||||
return makeElement({
|
||||
tag: "li",
|
||||
children: children
|
||||
children: children,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ export function Margin(children: Element | Element[]): Element {
|
|||
return makeElement({
|
||||
tag: "div",
|
||||
class: "uk-margin",
|
||||
children: children
|
||||
children: children,
|
||||
});
|
||||
}
|
|
@ -18,23 +18,35 @@ export function NavBar(): HTMLElement {
|
|||
tag: "ul",
|
||||
class: "uk-navbar-nav",
|
||||
children: [
|
||||
ListItem(makeElement({
|
||||
ListItem(
|
||||
makeElement({
|
||||
tag: "a",
|
||||
text: i18next.t("home_btn"),
|
||||
onclick: () => { changePage("HOME"); }
|
||||
})),
|
||||
ListItem(makeElement({
|
||||
onclick: () => {
|
||||
changePage("HOME");
|
||||
},
|
||||
}),
|
||||
),
|
||||
ListItem(
|
||||
makeElement({
|
||||
tag: "a",
|
||||
text: i18next.t("back_btn"),
|
||||
onclick: () => { (pageState.currentPage as Page).goBack(); }
|
||||
})),
|
||||
ListItem(makeElement({
|
||||
onclick: () => {
|
||||
(pageState.currentPage as Page).goBack();
|
||||
},
|
||||
}),
|
||||
),
|
||||
ListItem(
|
||||
makeElement({
|
||||
tag: "a",
|
||||
text: i18next.t("refresh_btn"),
|
||||
onclick: () => { changePage(pageState.currentPageString); }
|
||||
})),
|
||||
]
|
||||
})
|
||||
onclick: () => {
|
||||
changePage(pageState.currentPageString);
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "div",
|
||||
|
@ -43,19 +55,22 @@ export function NavBar(): HTMLElement {
|
|||
tag: "ul",
|
||||
class: "uk-navbar-nav",
|
||||
children: [
|
||||
ListItem(makeElement({
|
||||
ListItem(
|
||||
makeElement({
|
||||
tag: "a",
|
||||
text: i18next.t("me_btn"),
|
||||
onclick: () => { changePage("ME"); }
|
||||
}))
|
||||
]
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
onclick: () => {
|
||||
changePage("ME");
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export function reloadNavBar(): void {
|
||||
document.querySelector("#navBar").replaceWith(NavBar());
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,6 @@ export function Option(label: string, value: string): HTMLElement {
|
|||
attributes: {
|
||||
label: label,
|
||||
value: value,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import { Margin } from "./Margin";
|
||||
import { makeElement } from "../htmlUtils";
|
||||
import QrScanner from 'qr-scanner';
|
||||
import QrScanner from "qr-scanner";
|
||||
|
||||
/* eslint-disable import/no-unresolved */
|
||||
// @ts-ignore
|
||||
import qrScannerWorkerSource from '!!raw-loader!qr-scanner/qr-scanner-worker.min.js';
|
||||
import qrScannerWorkerSource from "!!raw-loader!qr-scanner/qr-scanner-worker.min.js";
|
||||
QrScanner.WORKER_PATH = URL.createObjectURL(new Blob([qrScannerWorkerSource]));
|
||||
|
||||
export interface QRScannerType extends HTMLElement {
|
||||
|
@ -13,19 +13,17 @@ export interface QRScannerType extends HTMLElement {
|
|||
|
||||
export async function QRScanner(onScan: (code: string) => void): Promise<QRScannerType> {
|
||||
const webcamVideo = makeElement({
|
||||
tag: "video"
|
||||
tag: "video",
|
||||
}) as HTMLVideoElement;
|
||||
|
||||
const QRInput = makeElement({
|
||||
tag: "div",
|
||||
children: [
|
||||
Margin(webcamVideo),
|
||||
]
|
||||
children: [Margin(webcamVideo)],
|
||||
}) as QRScannerType;
|
||||
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
facingMode: 'environment',
|
||||
facingMode: "environment",
|
||||
},
|
||||
audio: false,
|
||||
});
|
||||
|
@ -43,7 +41,7 @@ export async function QRScanner(onScan: (code: string) => void): Promise<QRScann
|
|||
track.stop();
|
||||
});
|
||||
} catch (_) {
|
||||
()=>{};
|
||||
() => {};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ type TileParams = {
|
|||
icon: string;
|
||||
iconText: string;
|
||||
onclick: () => void;
|
||||
}
|
||||
};
|
||||
|
||||
export function Tile(params: TileParams): HTMLElement {
|
||||
if (!params.condition) return;
|
||||
|
@ -28,17 +28,17 @@ export function Tile(params: TileParams): HTMLElement {
|
|||
class: ["uk-icon", "uk-margin-small-left"],
|
||||
attributes: {
|
||||
"uk-icon": `icon: ${params.icon}`,
|
||||
"role": "img",
|
||||
"aria-label": params.iconText
|
||||
}
|
||||
})
|
||||
role: "img",
|
||||
"aria-label": params.iconText,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "span",
|
||||
class: "uk-text-muted",
|
||||
text: params.description
|
||||
})
|
||||
]
|
||||
})
|
||||
text: params.description,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
import { de, enGB, fr, it, nl, ru } from 'date-fns/locale'
|
||||
import { formatDistance as formatDistanceReal} from 'date-fns';
|
||||
import { de, enGB, fr, it, nl, ru } from "date-fns/locale";
|
||||
import { formatDistance as formatDistanceReal } from "date-fns";
|
||||
import { pageState } from "./globalPageState";
|
||||
|
||||
function getLocale(): Locale {
|
||||
return {
|
||||
"en": enGB,
|
||||
"fr": fr,
|
||||
"nl": nl,
|
||||
"ru": ru,
|
||||
"de": de,
|
||||
"it": it,
|
||||
en: enGB,
|
||||
fr: fr,
|
||||
nl: nl,
|
||||
ru: ru,
|
||||
de: de,
|
||||
it: it,
|
||||
}[pageState.language];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
import { getObjectKeys } from "./utils";
|
||||
|
||||
type optionsFunctionsObject = {
|
||||
[key: string]: (e: Element, arg: unknown) => void
|
||||
}
|
||||
[key: string]: (e: Element, arg: unknown) => void;
|
||||
};
|
||||
|
||||
const optionsFunctions: optionsFunctionsObject = {
|
||||
"class": (e: Element, arg: string | string[]) => {
|
||||
class: (e: Element, arg: string | string[]) => {
|
||||
if (!Array.isArray(arg)) {
|
||||
arg = String(arg).split(" ");
|
||||
}
|
||||
e.classList.add(...arg);
|
||||
},
|
||||
"id": (e: Element, arg: string) => e.id = arg,
|
||||
"html": (e: Element, arg: string) => e.innerHTML = arg,
|
||||
"onclick": (e: Element, arg: () => void) => (e as HTMLButtonElement).onclick = arg,
|
||||
"attributes": setElementAttributes,
|
||||
"text": (e: Element, arg: string) => (e as HTMLParagraphElement).innerText = arg,
|
||||
"children": (e: Element, arg: Element | Element[]) => {
|
||||
id: (e: Element, arg: string) => (e.id = arg),
|
||||
html: (e: Element, arg: string) => (e.innerHTML = arg),
|
||||
onclick: (e: Element, arg: () => void) => ((e as HTMLButtonElement).onclick = arg),
|
||||
attributes: setElementAttributes,
|
||||
text: (e: Element, arg: string) => ((e as HTMLParagraphElement).innerText = arg),
|
||||
children: (e: Element, arg: Element | Element[]) => {
|
||||
if (Array.isArray(arg)) {
|
||||
arg.forEach(child => {
|
||||
arg.forEach((child) => {
|
||||
if (child != null) e.appendChild(child);
|
||||
});
|
||||
} else {
|
||||
if (arg != null) e.appendChild(arg);
|
||||
}
|
||||
},
|
||||
"thenRun": (e: Element, arg: (e: Element) => void) => arg(e),
|
||||
}
|
||||
thenRun: (e: Element, arg: (e: Element) => void) => arg(e),
|
||||
};
|
||||
|
||||
interface ElementInfo {
|
||||
condition?: boolean;
|
||||
|
@ -43,7 +43,11 @@ interface ElementInfo {
|
|||
}
|
||||
|
||||
export function makeElement(elementInfo: ElementInfo): HTMLElement {
|
||||
if ("condition" in elementInfo) { if (!elementInfo.condition) { return null; } }
|
||||
if ("condition" in elementInfo) {
|
||||
if (!elementInfo.condition) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const element = document.createElement(elementInfo.tag);
|
||||
|
||||
for (const key of Object.getOwnPropertyNames(elementInfo)) {
|
||||
|
@ -61,9 +65,10 @@ export function setElementAttributes(element: Element, attributes: Record<string
|
|||
}
|
||||
}
|
||||
|
||||
export const fileToBase64 = (file: File): Promise<string> => new Promise((resolve, reject) => {
|
||||
export const fileToBase64 = (file: File): Promise<string> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result as string);
|
||||
reader.onerror = error => reject(error);
|
||||
});
|
||||
reader.onerror = (error) => reject(error);
|
||||
});
|
||||
|
|
67
src/main.ts
67
src/main.ts
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
"use strict";
|
||||
|
||||
// JS & CSS
|
||||
|
||||
|
@ -17,32 +17,31 @@ import "prismjs/components/prism-json";
|
|||
Prism.highlightAll();
|
||||
/* eslint-enable */
|
||||
|
||||
import {
|
||||
changePage,
|
||||
renderPage,
|
||||
} from "./pageUtils";
|
||||
import { NavBar } from "./elements/NavBar";
|
||||
import { changePage, renderPage } from "./pageUtils";
|
||||
import { getSealStatus } from "./api/sys/getSealStatus";
|
||||
import { makeElement } from "./htmlUtils";
|
||||
import { pageState } from "./globalPageState";
|
||||
import { playground } from "./playground";
|
||||
import { NavBar } from "./elements/NavBar";
|
||||
|
||||
// Translations
|
||||
import { formatDistance } from './formatDistance';
|
||||
import i18next from 'i18next';
|
||||
import { formatDistance } from "./formatDistance";
|
||||
import i18next from "i18next";
|
||||
|
||||
// @ts-ignore
|
||||
import translations from './translations/index.mjs';
|
||||
import translations from "./translations/index.mjs";
|
||||
|
||||
declare global {
|
||||
interface Window { pageContent: Element; }
|
||||
interface Window {
|
||||
pageContent: Element;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onLoad(): void {
|
||||
document.body.innerHTML = "";
|
||||
document.body.appendChild(NavBar());
|
||||
document.body.appendChild(makeElement({
|
||||
document.body.appendChild(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
class: ["uk-container", "uk-container-medium", "uk-align-center"],
|
||||
children: makeElement({
|
||||
|
@ -53,15 +52,16 @@ function onLoad(): void {
|
|||
tag: "h3",
|
||||
class: "uk-card-title",
|
||||
id: "pageTitle",
|
||||
text: ""
|
||||
text: "",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "div",
|
||||
id: "pageContent"
|
||||
})
|
||||
]
|
||||
})
|
||||
}));
|
||||
id: "pageContent",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
window.pageContent = document.querySelector("#pageContent");
|
||||
|
||||
|
@ -73,7 +73,9 @@ function onLoad(): void {
|
|||
|
||||
setInterval(() => {
|
||||
if (pageState.currentPageString != "UNSEAL") {
|
||||
if (pageState.apiURL.length != 0) { return; }
|
||||
if (pageState.apiURL.length != 0) {
|
||||
return;
|
||||
}
|
||||
void getSealStatus().then((sealStatus) => {
|
||||
if (sealStatus.sealed) {
|
||||
changePage("UNSEAL");
|
||||
|
@ -84,23 +86,32 @@ function onLoad(): void {
|
|||
}, 5000);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
function () {
|
||||
console.log("Loading...");
|
||||
// @ts-expect-error
|
||||
console.log("Build Data:", BUILD_STRING);
|
||||
void i18next.init({
|
||||
void i18next
|
||||
.init({
|
||||
lng: pageState.language,
|
||||
fallbackLng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
// @ts-ignore
|
||||
resources: Object.fromEntries(Object.entries(translations).map(([k, v]) => [k, { translation: v }])),
|
||||
resources: Object.fromEntries(
|
||||
Object.entries(translations).map(([k, v]) => [k, { translation: v }]),
|
||||
),
|
||||
interpolation: {
|
||||
format: function (value: unknown, format, _): string {
|
||||
if (format === 'until_date' && value instanceof Date) return formatDistance(new Date(), new Date(value));
|
||||
if (format === "until_date" && value instanceof Date)
|
||||
return formatDistance(new Date(), new Date(value));
|
||||
return value as string;
|
||||
}
|
||||
}
|
||||
}).then(function (_) {
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(function (_) {
|
||||
onLoad();
|
||||
});
|
||||
}, false);
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
|
|
@ -5,8 +5,8 @@ import { lookupSelf } from "./api/sys/lookupSelf";
|
|||
import { makeElement } from "./htmlUtils";
|
||||
import { pageState } from "./globalPageState";
|
||||
import ClipboardJS from "clipboard";
|
||||
import UIkit from 'uikit';
|
||||
import i18next from 'i18next';
|
||||
import UIkit from "uikit";
|
||||
import i18next from "i18next";
|
||||
|
||||
async function prePageChecksReal() {
|
||||
if (pageState.language.length == 0) {
|
||||
|
@ -27,7 +27,7 @@ async function prePageChecksReal() {
|
|||
try {
|
||||
await lookupSelf();
|
||||
} catch (e) {
|
||||
changePage("LOGIN")
|
||||
changePage("LOGIN");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -36,26 +36,29 @@ export async function prePageChecks(): Promise<boolean> {
|
|||
try {
|
||||
await prePageChecksReal();
|
||||
} catch (e) {
|
||||
console.log("OHNO", e)
|
||||
console.log("OHNO", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function addClipboardNotifications(clipboard: ClipboardJS, timeout = 1000): void {
|
||||
clipboard.on('success', () => {
|
||||
clipboard.on("success", () => {
|
||||
UIkit.notification(i18next.t("notification_copy_success"), {
|
||||
status: 'success',
|
||||
timeout: timeout
|
||||
status: "success",
|
||||
timeout: timeout,
|
||||
});
|
||||
});
|
||||
clipboard.on('error', function (e: Error) {
|
||||
UIkit.notification(i18next.t("notification_copy_error", {
|
||||
"error": e.message
|
||||
}), {
|
||||
status: 'danger',
|
||||
timeout: timeout
|
||||
});
|
||||
clipboard.on("error", function (e: Error) {
|
||||
UIkit.notification(
|
||||
i18next.t("notification_copy_error", {
|
||||
error: e.message,
|
||||
}),
|
||||
{
|
||||
status: "danger",
|
||||
timeout: timeout,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -69,9 +72,9 @@ export function setErrorText(text: string): void {
|
|||
}
|
||||
UIkit.notification({
|
||||
message: `Error: ${text}`,
|
||||
status: 'danger',
|
||||
pos: 'top-center',
|
||||
timeout: 2000
|
||||
status: "danger",
|
||||
pos: "top-center",
|
||||
timeout: 2000,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -88,13 +91,13 @@ export function changePage(page: string, shouldSwitch = true): void {
|
|||
export function renderPage(): void {
|
||||
document.documentElement.dir = pageState.pageDirection;
|
||||
console.log("Rendering Page: ", (pageState.currentPage as Page).name);
|
||||
(document.querySelector("#pageContent") ).innerHTML = "";
|
||||
document.querySelector("#pageContent").innerHTML = "";
|
||||
setPageTitle((pageState.currentPage as Page).name);
|
||||
(pageState.currentPage as Page).render();
|
||||
}
|
||||
|
||||
export function setPageTitle(title: string | HTMLElement): void {
|
||||
const pageTitle = (document.getElementById("pageTitle") );
|
||||
const pageTitle = document.getElementById("pageTitle");
|
||||
pageTitle.innerHTML = "";
|
||||
if (typeof title === "string") {
|
||||
pageTitle.innerText = title.toString();
|
||||
|
@ -106,7 +109,8 @@ export function setPageTitle(title: string | HTMLElement): void {
|
|||
function currentTitleSecretText() {
|
||||
let currentSecretText = pageState.currentSecret;
|
||||
currentSecretText += (pageState.currentPage as Page).titleSuffix;
|
||||
if (pageState.currentSecretVersion !== null) currentSecretText += ` (v${pageState.currentSecretVersion})`;
|
||||
if (pageState.currentSecretVersion !== null)
|
||||
currentSecretText += ` (v${pageState.currentSecretVersion})`;
|
||||
return currentSecretText;
|
||||
}
|
||||
|
||||
|
@ -122,14 +126,17 @@ export function setTitleElement(pageState: PageState): void {
|
|||
pageState.currentSecret = "";
|
||||
pageState.currentSecretVersion = null;
|
||||
|
||||
if (pageState.currentMountType.startsWith("kv") || pageState.currentMountType == "cubbyhole") {
|
||||
if (
|
||||
pageState.currentMountType.startsWith("kv") ||
|
||||
pageState.currentMountType == "cubbyhole"
|
||||
) {
|
||||
changePage("KEY_VALUE_VIEW");
|
||||
} else if (pageState.currentMountType == "totp") {
|
||||
changePage("TOTP");
|
||||
} else if (pageState.currentMountType == "transit") {
|
||||
changePage("TRANSIT_VIEW");
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
...pageState.currentSecretPath.map(function (secretPath, index, secretPaths) {
|
||||
return makeElement({
|
||||
|
@ -141,21 +148,21 @@ export function setTitleElement(pageState: PageState): void {
|
|||
pageState.currentSecretPath = secretPaths.slice(0, index + 1);
|
||||
changePage("KEY_VALUE_VIEW");
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}),
|
||||
makeElement({
|
||||
tag: "span",
|
||||
condition: pageState.currentSecret.length != 0,
|
||||
text: currentTitleSecretText()
|
||||
})
|
||||
]
|
||||
text: currentTitleSecretText(),
|
||||
}),
|
||||
],
|
||||
});
|
||||
setPageTitle(titleElement);
|
||||
}
|
||||
|
||||
export function setPageContent(content: string | HTMLElement): void {
|
||||
const pageContent = (document.getElementById("pageContent") );
|
||||
const pageContent = document.getElementById("pageContent");
|
||||
if (typeof content === "string") {
|
||||
pageContent.innerHTML = content;
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { lookupSelf } from "../api/sys/lookupSelf";
|
|||
import { makeElement } from "../htmlUtils";
|
||||
import { pageState } from "../globalPageState";
|
||||
import { sortedObjectMap } from "../utils";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export class HomePage extends Page {
|
||||
constructor() {
|
||||
|
@ -15,7 +15,7 @@ export class HomePage extends Page {
|
|||
setPageContent("");
|
||||
if (!(await prePageChecks())) return;
|
||||
|
||||
const homePageContent = makeElement({tag: "div"});
|
||||
const homePageContent = makeElement({ tag: "div" });
|
||||
setPageContent(homePageContent);
|
||||
const textList = makeElement({
|
||||
tag: "ul",
|
||||
|
@ -25,8 +25,8 @@ export class HomePage extends Page {
|
|||
tag: "li",
|
||||
children: makeElement({
|
||||
tag: "span",
|
||||
html: i18next.t("vaulturl_text", {"text": pageState.apiURL})
|
||||
})
|
||||
html: i18next.t("vaulturl_text", { text: pageState.apiURL }),
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "li",
|
||||
|
@ -35,19 +35,23 @@ export class HomePage extends Page {
|
|||
text: i18next.t("password_generator_btn"),
|
||||
onclick: () => {
|
||||
changePage("PW_GEN");
|
||||
}
|
||||
})
|
||||
})
|
||||
]
|
||||
},
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
homePageContent.appendChild(textList);
|
||||
|
||||
try {
|
||||
const selfTokenInfo = await lookupSelf();
|
||||
textList.appendChild(makeElement({
|
||||
textList.appendChild(
|
||||
makeElement({
|
||||
tag: "li",
|
||||
text: i18next.t("your_token_expires_in", {"date": new Date(selfTokenInfo.expire_time)})
|
||||
}));
|
||||
text: i18next.t("your_token_expires_in", {
|
||||
date: new Date(selfTokenInfo.expire_time),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
const error = e as Error;
|
||||
setErrorText(error.message);
|
||||
|
@ -62,7 +66,10 @@ export class HomePage extends Page {
|
|||
pageState.currentSecret = "";
|
||||
pageState.currentSecretVersion = null;
|
||||
|
||||
const navList = makeElement({ tag: "ul", class: ["uk-nav", "uk-nav-default", "uk-margin-top"] });
|
||||
const navList = makeElement({
|
||||
tag: "ul",
|
||||
class: ["uk-nav", "uk-nav-default", "uk-margin-top"],
|
||||
});
|
||||
homePageContent.appendChild(navList);
|
||||
|
||||
const mounts = await getMounts();
|
||||
|
@ -70,10 +77,10 @@ export class HomePage extends Page {
|
|||
const mountsMap = sortedObjectMap(mounts);
|
||||
|
||||
mountsMap.forEach(function (mount: MountType, baseMount) {
|
||||
if (typeof mount != 'object') return;
|
||||
if (typeof mount != "object") return;
|
||||
if (mount == null) return;
|
||||
if (!("type" in mount)) return;
|
||||
if (!(["kv", "totp", "transit", "cubbyhole"].includes(mount.type))) return;
|
||||
if (!["kv", "totp", "transit", "cubbyhole"].includes(mount.type)) return;
|
||||
|
||||
const mountType = mount.type == "kv" ? "kv-v" + String(mount.options.version) : mount.type;
|
||||
|
||||
|
@ -85,15 +92,16 @@ export class HomePage extends Page {
|
|||
} else if (mount.type == "totp") {
|
||||
linkText = `TOTP - ${baseMount}`;
|
||||
linkPage = "TOTP";
|
||||
} else if (mount.type == "transit"){
|
||||
} else if (mount.type == "transit") {
|
||||
linkText = `Transit - ${baseMount}`;
|
||||
linkPage = "TRANSIT_VIEW";
|
||||
} else if (mount.type == "cubbyhole"){
|
||||
} else if (mount.type == "cubbyhole") {
|
||||
linkText = `Cubbyhole - ${baseMount}`;
|
||||
linkPage = "KEY_VALUE_VIEW";
|
||||
}
|
||||
|
||||
navList.appendChild(makeElement({
|
||||
navList.appendChild(
|
||||
makeElement({
|
||||
tag: "li",
|
||||
children: makeElement({
|
||||
tag: "a",
|
||||
|
@ -102,9 +110,10 @@ export class HomePage extends Page {
|
|||
pageState.currentBaseMount = baseMount;
|
||||
pageState.currentMountType = mountType;
|
||||
changePage(linkPage);
|
||||
}
|
||||
})
|
||||
}));
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
get name(): string {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { changePage, setPageContent, setTitleElement } from "../../pageUtils";
|
|||
import { deleteSecret } from "../../api/kv/deleteSecret";
|
||||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export class KeyValueDeletePage extends Page {
|
||||
constructor() {
|
||||
|
@ -20,12 +20,13 @@ export class KeyValueDeletePage extends Page {
|
|||
}
|
||||
render(): void {
|
||||
setTitleElement(pageState);
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
children: [
|
||||
makeElement({
|
||||
tag: "h5",
|
||||
text: i18next.t("kv_delete_text")
|
||||
text: i18next.t("kv_delete_text"),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
|
@ -41,10 +42,11 @@ export class KeyValueDeletePage extends Page {
|
|||
).then(() => {
|
||||
this.goBack();
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
]
|
||||
}));
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
get titleSuffix(): string {
|
||||
return i18next.t("kv_delete_suffix");
|
||||
|
|
|
@ -3,7 +3,7 @@ import { changePage, setErrorText, setPageContent, setTitleElement } from "../..
|
|||
import { createOrUpdateSecret } from "../../api/kv/createOrUpdateSecret";
|
||||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export class KeyValueNewPage extends Page {
|
||||
constructor() {
|
||||
|
@ -32,14 +32,14 @@ export class KeyValueNewPage extends Page {
|
|||
required: "true",
|
||||
type: "text",
|
||||
placeholder: i18next.t("kv_new_path"),
|
||||
name: "path"
|
||||
}
|
||||
})
|
||||
name: "path",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
|
@ -47,9 +47,9 @@ export class KeyValueNewPage extends Page {
|
|||
text: i18next.t("kv_new_create_btn"),
|
||||
attributes: {
|
||||
type: "submit",
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
}),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
setPageContent(this.addKVNewForm);
|
||||
|
||||
|
@ -65,7 +65,7 @@ export class KeyValueNewPage extends Page {
|
|||
let keyData = {};
|
||||
|
||||
if (["kv-v1", "cubbyhole"].includes(pageState.currentMountType)) {
|
||||
keyData = { "key": "value" };
|
||||
keyData = { key: "value" };
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -74,14 +74,13 @@ export class KeyValueNewPage extends Page {
|
|||
pageState.currentMountType,
|
||||
pageState.currentSecretPath,
|
||||
path,
|
||||
keyData
|
||||
)
|
||||
keyData,
|
||||
);
|
||||
changePage("KEY_VALUE_VIEW");
|
||||
} catch (e: unknown) {
|
||||
const error = e as Error;
|
||||
setErrorText(error.message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get titleSuffix(): string {
|
||||
|
|
|
@ -8,8 +8,7 @@ import { pageState } from "../../globalPageState";
|
|||
import { sortedObjectMap } from "../../utils";
|
||||
import { undeleteSecret } from "../../api/kv/undeleteSecret";
|
||||
import Prism from "prismjs";
|
||||
import i18next from 'i18next';
|
||||
|
||||
import i18next from "i18next";
|
||||
|
||||
export class KeyValueSecretPage extends Page {
|
||||
constructor() {
|
||||
|
@ -23,72 +22,86 @@ export class KeyValueSecretPage extends Page {
|
|||
pageState.currentSecret = "";
|
||||
changePage("KEY_VALUE_VIEW");
|
||||
}
|
||||
|
||||
}
|
||||
async render(): Promise<void> {
|
||||
setTitleElement(pageState);
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
children: [
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "buttonsBlock"
|
||||
id: "buttonsBlock",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("kv_secret_loading"),
|
||||
id: "loadingText"
|
||||
id: "loadingText",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "div",
|
||||
id: "kvList"
|
||||
id: "kvList",
|
||||
}),
|
||||
]
|
||||
}));
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const buttonsBlock = document.querySelector("#buttonsBlock");
|
||||
const kvList = document.querySelector("#kvList");
|
||||
let isSecretNestedJson = false;
|
||||
const caps = await getCapabilities(pageState.currentBaseMount, pageState.currentSecretPath, pageState.currentSecret);
|
||||
const caps = await getCapabilities(
|
||||
pageState.currentBaseMount,
|
||||
pageState.currentSecretPath,
|
||||
pageState.currentSecret,
|
||||
);
|
||||
if (caps.includes("delete")) {
|
||||
let deleteButtonText = i18next.t("kv_secret_delete_btn");
|
||||
if (pageState.currentMountType == "kv-v2" && pageState.currentSecretVersion == null) {
|
||||
deleteButtonText = i18next.t("kv_secret_delete_all_btn");
|
||||
} else if (pageState.currentMountType == "kv-v2" && pageState.currentSecretVersion != null) {
|
||||
deleteButtonText = i18next.t(
|
||||
"kv_secret_delete_version_btn",
|
||||
{
|
||||
"version": pageState.currentSecretVersion
|
||||
deleteButtonText = i18next.t("kv_secret_delete_version_btn", {
|
||||
version: pageState.currentSecretVersion,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
buttonsBlock.appendChild(makeElement({
|
||||
buttonsBlock.appendChild(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
id: "deleteButton",
|
||||
class: ["uk-button", "uk-button-danger"],
|
||||
onclick: () => { changePage("KEY_VALUE_DELETE"); },
|
||||
text: deleteButtonText
|
||||
}));
|
||||
onclick: () => {
|
||||
changePage("KEY_VALUE_DELETE");
|
||||
},
|
||||
text: deleteButtonText,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (caps.includes("update")) {
|
||||
if (pageState.currentSecretVersion == null) {
|
||||
buttonsBlock.appendChild(makeElement({
|
||||
buttonsBlock.appendChild(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
id: "editButton",
|
||||
class: ["uk-button", "uk-margin", "uk-button-primary"],
|
||||
onclick: () => { changePage("KEY_VALUE_SECRET_EDIT"); },
|
||||
text: i18next.t("kv_secret_edit_btn")
|
||||
}));
|
||||
onclick: () => {
|
||||
changePage("KEY_VALUE_SECRET_EDIT");
|
||||
},
|
||||
text: i18next.t("kv_secret_edit_btn"),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (pageState.currentMountType == "kv-v2") {
|
||||
buttonsBlock.appendChild(makeElement({
|
||||
buttonsBlock.appendChild(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
id: "versionsButton",
|
||||
class: ["uk-button", "uk-button-secondary"],
|
||||
onclick: () => { changePage("KEY_VALUE_VERSIONS"); },
|
||||
text: i18next.t("kv_secret_versions_btn")
|
||||
}));
|
||||
onclick: () => {
|
||||
changePage("KEY_VALUE_VERSIONS");
|
||||
},
|
||||
text: i18next.t("kv_secret_versions_btn"),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
void getSecret(
|
||||
|
@ -96,18 +109,21 @@ export class KeyValueSecretPage extends Page {
|
|||
pageState.currentMountType,
|
||||
pageState.currentSecretPath,
|
||||
pageState.currentSecret,
|
||||
pageState.currentSecretVersion
|
||||
).then(secretInfo => {
|
||||
pageState.currentSecretVersion,
|
||||
).then((secretInfo) => {
|
||||
if (secretInfo == null && pageState.currentMountType == "kv-v2") {
|
||||
document.querySelector("#buttonsBlock").remove();
|
||||
document.getElementById("loadingText").remove();
|
||||
|
||||
kvList.appendChild(makeElement({
|
||||
kvList.appendChild(
|
||||
makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("kv_secret_deleted_text")
|
||||
}));
|
||||
text: i18next.t("kv_secret_deleted_text"),
|
||||
}),
|
||||
);
|
||||
|
||||
kvList.appendChild(makeElement({
|
||||
kvList.appendChild(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
text: i18next.t("kv_secret_restore_btn"),
|
||||
id: "restoreButton",
|
||||
|
@ -117,32 +133,35 @@ export class KeyValueSecretPage extends Page {
|
|||
pageState.currentBaseMount,
|
||||
pageState.currentSecretPath,
|
||||
pageState.currentSecret,
|
||||
pageState.currentSecretVersion
|
||||
).then(_ => {
|
||||
pageState.currentSecretVersion,
|
||||
).then((_) => {
|
||||
changePage(pageState.currentPageString);
|
||||
});
|
||||
},
|
||||
}));
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const secretsMap = sortedObjectMap(secretInfo);
|
||||
|
||||
for (const value of secretsMap.values()) {
|
||||
if (typeof value == 'object') isSecretNestedJson = true;
|
||||
if (typeof value == "object") isSecretNestedJson = true;
|
||||
}
|
||||
|
||||
if (isSecretNestedJson) {
|
||||
const jsonText = JSON.stringify(
|
||||
sortedObjectMap(secretsMap as unknown as Record<string, unknown>),
|
||||
null,
|
||||
4
|
||||
4,
|
||||
);
|
||||
kvList.appendChild(makeElement({
|
||||
kvList.appendChild(
|
||||
makeElement({
|
||||
tag: "pre",
|
||||
class: ["code-block", "language-json", "line-numbers"],
|
||||
html: Prism.highlight(jsonText, Prism.languages.json, 'json')
|
||||
}));
|
||||
html: Prism.highlight(jsonText, Prism.languages.json, "json"),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
secretsMap.forEach((value: string, key: string) => {
|
||||
const kvListElement = this.makeKVListElement(key, value);
|
||||
|
@ -156,7 +175,7 @@ export class KeyValueSecretPage extends Page {
|
|||
return makeElement({
|
||||
tag: "div",
|
||||
class: ["uk-grid", "uk-grid-small", "uk-text-left"],
|
||||
children: [CopyableInputBox(key), CopyableInputBox(value)]
|
||||
children: [CopyableInputBox(key), CopyableInputBox(value)],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { getSecret } from "../../api/kv/getSecret";
|
|||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import { sortedObjectMap, verifyJSONString } from "../../utils";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export class KeyValueSecretEditPage extends Page {
|
||||
constructor() {
|
||||
|
@ -19,18 +19,19 @@ export class KeyValueSecretEditPage extends Page {
|
|||
setTitleElement(pageState);
|
||||
const loadingText = makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("kv_sec_edit_loading")
|
||||
text: i18next.t("kv_sec_edit_loading"),
|
||||
});
|
||||
const editor = makeElement({
|
||||
tag: "div",
|
||||
class: ["editor", "language-json"]
|
||||
class: ["editor", "language-json"],
|
||||
});
|
||||
const saveButton = makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
text: i18next.t("kv_sec_edit_btn")
|
||||
text: i18next.t("kv_sec_edit_btn"),
|
||||
});
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
children: [
|
||||
loadingText,
|
||||
|
@ -38,22 +39,23 @@ export class KeyValueSecretEditPage extends Page {
|
|||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: ["uk-text-danger", "uk-margin-top"]
|
||||
class: ["uk-text-danger", "uk-margin-top"],
|
||||
}),
|
||||
saveButton
|
||||
]
|
||||
}));
|
||||
saveButton,
|
||||
],
|
||||
}),
|
||||
);
|
||||
void getSecret(
|
||||
pageState.currentBaseMount,
|
||||
pageState.currentMountType,
|
||||
pageState.currentSecretPath,
|
||||
pageState.currentSecret,
|
||||
).then(secretInfo => {
|
||||
).then((secretInfo) => {
|
||||
loadingText.remove();
|
||||
|
||||
const secretsJSON = JSON.stringify(sortedObjectMap(secretInfo), null, 4);
|
||||
|
||||
const jar = CodeJar(editor, () => { }, { tab: ' '.repeat(4) });
|
||||
const jar = CodeJar(editor, () => {}, { tab: " ".repeat(4) });
|
||||
jar.updateCode(secretsJSON);
|
||||
saveButton.onclick = function () {
|
||||
if (!verifyJSONString(jar.toString())) {
|
||||
|
@ -66,11 +68,13 @@ export class KeyValueSecretEditPage extends Page {
|
|||
pageState.currentMountType,
|
||||
pageState.currentSecretPath,
|
||||
pageState.currentSecret,
|
||||
JSON.parse(jar.toString())
|
||||
).then(_ => {
|
||||
JSON.parse(jar.toString()),
|
||||
)
|
||||
.then((_) => {
|
||||
changePage("KEY_VALUE_SECRET");
|
||||
return;
|
||||
}).catch((e: Error) => {
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
setErrorText(e.message);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -4,8 +4,7 @@ import { getSecretMetadata } from "../../api/kv/getSecretMetadata";
|
|||
import { makeElement } from "../../htmlUtils";
|
||||
import { objectToMap } from "../../utils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
|
||||
import i18next from "i18next";
|
||||
|
||||
export class KeyValueVersionsPage extends Page {
|
||||
constructor() {
|
||||
|
@ -23,18 +22,19 @@ export class KeyValueVersionsPage extends Page {
|
|||
const versionsList = makeElement({
|
||||
tag: "ul",
|
||||
id: "versionsList",
|
||||
class: ["uk-nav", "uk-nav-default"]
|
||||
class: ["uk-nav", "uk-nav-default"],
|
||||
});
|
||||
setPageContent(versionsList);
|
||||
|
||||
const metadata = await getSecretMetadata(
|
||||
pageState.currentBaseMount,
|
||||
pageState.currentSecretPath,
|
||||
pageState.currentSecret
|
||||
pageState.currentSecret,
|
||||
);
|
||||
|
||||
objectToMap(metadata.versions).forEach((_, ver) => {
|
||||
versionsList.appendChild(makeElement({
|
||||
versionsList.appendChild(
|
||||
makeElement({
|
||||
tag: "li",
|
||||
children: makeElement({
|
||||
tag: "a",
|
||||
|
@ -42,9 +42,10 @@ export class KeyValueVersionsPage extends Page {
|
|||
onclick: () => {
|
||||
pageState.currentSecretVersion = ver;
|
||||
changePage("KEY_VALUE_SECRET");
|
||||
}
|
||||
})
|
||||
}));
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ import { changePage, setErrorText, setPageContent, setTitleElement } from "../..
|
|||
import { getSecrets } from "../../api/kv/getSecrets";
|
||||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
|
||||
import i18next from "i18next";
|
||||
|
||||
export class KeyValueViewPage extends Page {
|
||||
constructor() {
|
||||
|
@ -28,10 +27,12 @@ export class KeyValueViewPage extends Page {
|
|||
setPageContent(kvViewPageContent);
|
||||
|
||||
if (pageState.currentMountType == "cubbyhole") {
|
||||
kvViewPageContent.appendChild(makeElement({
|
||||
kvViewPageContent.appendChild(
|
||||
makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("kv_view_cubbyhole_text"),
|
||||
}));
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const newButton = makeElement({
|
||||
|
@ -40,7 +41,7 @@ export class KeyValueViewPage extends Page {
|
|||
class: ["uk-button", "uk-button-primary", "uk-margin-bottom"],
|
||||
onclick: () => {
|
||||
changePage("KEY_VALUE_NEW_SECRET");
|
||||
}
|
||||
},
|
||||
});
|
||||
kvViewPageContent.appendChild(newButton);
|
||||
|
||||
|
@ -51,7 +52,8 @@ export class KeyValueViewPage extends Page {
|
|||
pageState.currentSecretPath,
|
||||
);
|
||||
|
||||
kvViewPageContent.appendChild(makeElement({
|
||||
kvViewPageContent.appendChild(
|
||||
makeElement({
|
||||
tag: "ul",
|
||||
class: ["uk-nav", "uk-nav-default"],
|
||||
children: [
|
||||
|
@ -69,12 +71,13 @@ export class KeyValueViewPage extends Page {
|
|||
pageState.currentSecret = secret;
|
||||
changePage("KEY_VALUE_SECRET");
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
});
|
||||
})
|
||||
]
|
||||
}));
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
const error = e as Error;
|
||||
if (error == DoesNotExistError) {
|
||||
|
@ -82,10 +85,12 @@ export class KeyValueViewPage extends Page {
|
|||
if (pageState.currentSecretPath.length != 0) {
|
||||
return this.goBack();
|
||||
} else {
|
||||
kvViewPageContent.appendChild(makeElement({
|
||||
kvViewPageContent.appendChild(
|
||||
makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("kv_view_none_here_text")
|
||||
}));
|
||||
text: i18next.t("kv_view_none_here_text"),
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
setErrorText(error.message);
|
||||
|
|
|
@ -6,7 +6,7 @@ import { lookupSelf } from "../api/sys/lookupSelf";
|
|||
import { makeElement } from "../htmlUtils";
|
||||
import { pageState } from "../globalPageState";
|
||||
import { usernameLogin } from "../api/auth/usernameLogin";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export class LoginPage extends Page {
|
||||
constructor() {
|
||||
|
@ -16,31 +16,36 @@ export class LoginPage extends Page {
|
|||
const tokenLoginForm = makeElement({
|
||||
tag: "form",
|
||||
children: [
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "input",
|
||||
class: ["uk-input", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
required: "true",
|
||||
type: "password",
|
||||
placeholder: i18next.t("token_input"),
|
||||
name: "token"
|
||||
}
|
||||
})),
|
||||
MarginInline(makeElement({
|
||||
name: "token",
|
||||
},
|
||||
}),
|
||||
),
|
||||
MarginInline(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
text: i18next.t("log_in_btn"),
|
||||
attributes: {
|
||||
type: "submit"
|
||||
}
|
||||
}))
|
||||
]
|
||||
type: "submit",
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
|
||||
const usernameLoginForm = makeElement({
|
||||
tag: "form",
|
||||
children: [
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "input",
|
||||
id: "usernameInput",
|
||||
class: ["uk-input", "uk-form-width-medium"],
|
||||
|
@ -48,10 +53,12 @@ export class LoginPage extends Page {
|
|||
required: "true",
|
||||
type: "text",
|
||||
placeholder: i18next.t("username_input"),
|
||||
name: "username"
|
||||
}
|
||||
})),
|
||||
Margin(makeElement({
|
||||
name: "username",
|
||||
},
|
||||
}),
|
||||
),
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "input",
|
||||
id: "passwordInput",
|
||||
class: ["uk-input", "uk-form-width-medium"],
|
||||
|
@ -59,21 +66,25 @@ export class LoginPage extends Page {
|
|||
required: "true",
|
||||
type: "password",
|
||||
placeholder: i18next.t("password_input"),
|
||||
name: "password"
|
||||
}
|
||||
})),
|
||||
MarginInline(makeElement({
|
||||
name: "password",
|
||||
},
|
||||
}),
|
||||
),
|
||||
MarginInline(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
text: i18next.t("log_in_btn"),
|
||||
attributes: {
|
||||
type: "submit"
|
||||
}
|
||||
}))
|
||||
]
|
||||
type: "submit",
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
children: [
|
||||
makeElement({
|
||||
|
@ -86,22 +97,22 @@ export class LoginPage extends Page {
|
|||
id: "tokenInput",
|
||||
children: makeElement({
|
||||
tag: "a",
|
||||
text: i18next.t("log_in_with_token")
|
||||
})
|
||||
text: i18next.t("log_in_with_token"),
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "li",
|
||||
children: makeElement({
|
||||
tag: "a",
|
||||
text: i18next.t("log_in_with_username")
|
||||
})
|
||||
})
|
||||
]
|
||||
text: i18next.t("log_in_with_username"),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "ul",
|
||||
|
@ -109,25 +120,28 @@ export class LoginPage extends Page {
|
|||
children: [
|
||||
makeElement({
|
||||
tag: "li",
|
||||
children: tokenLoginForm
|
||||
children: tokenLoginForm,
|
||||
}),
|
||||
makeElement({
|
||||
tag: "li",
|
||||
children: usernameLoginForm
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
}));
|
||||
children: usernameLoginForm,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
tokenLoginForm.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(tokenLoginForm);
|
||||
const token = formData.get("token");
|
||||
pageState.token = token as string;
|
||||
lookupSelf().then(_ => {
|
||||
lookupSelf()
|
||||
.then((_) => {
|
||||
changePage("HOME");
|
||||
}).catch((e: Error) => {
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
document.getElementById("tokenInput").classList.add("uk-form-danger");
|
||||
if (e.message == "permission denied") {
|
||||
setErrorText(i18next.t("token_login_error"));
|
||||
|
@ -139,13 +153,12 @@ export class LoginPage extends Page {
|
|||
usernameLoginForm.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(usernameLoginForm);
|
||||
usernameLogin(
|
||||
formData.get("username") as string,
|
||||
formData.get("password") as string,
|
||||
).then(res => {
|
||||
usernameLogin(formData.get("username") as string, formData.get("password") as string)
|
||||
.then((res) => {
|
||||
pageState.token = res;
|
||||
changePage("HOME");
|
||||
}).catch((e: Error) => {
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
document.getElementById("usernameInput").classList.add("uk-form-danger");
|
||||
document.getElementById("passwordInput").classList.add("uk-form-danger");
|
||||
setErrorText(e.message);
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
import { Page } from "../types/Page";
|
||||
import { addClipboardNotifications, changePage, prePageChecks, setErrorText, setPageContent } from "../pageUtils";
|
||||
import {
|
||||
addClipboardNotifications,
|
||||
changePage,
|
||||
prePageChecks,
|
||||
setErrorText,
|
||||
setPageContent,
|
||||
} from "../pageUtils";
|
||||
import { getCapabilitiesPath } from "../api/sys/getCapabilities";
|
||||
import { makeElement } from "../htmlUtils";
|
||||
import { pageState } from "../globalPageState";
|
||||
import { renewSelf } from "../api/sys/renewSelf";
|
||||
import { sealVault } from "../api/sys/sealVault";
|
||||
import ClipboardJS from "clipboard";
|
||||
import i18next from 'i18next';
|
||||
|
||||
import i18next from "i18next";
|
||||
|
||||
export class MePage extends Page {
|
||||
constructor() {
|
||||
|
@ -16,7 +21,8 @@ export class MePage extends Page {
|
|||
|
||||
async render(): Promise<void> {
|
||||
if (!(await prePageChecks())) return;
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "ul",
|
||||
class: "uk-nav",
|
||||
children: [
|
||||
|
@ -28,8 +34,8 @@ export class MePage extends Page {
|
|||
onclick: () => {
|
||||
pageState.token = "";
|
||||
changePage("HOME");
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "li",
|
||||
|
@ -42,8 +48,8 @@ export class MePage extends Page {
|
|||
thenRun: (e) => {
|
||||
const clipboard = new ClipboardJS(e);
|
||||
addClipboardNotifications(clipboard);
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "li",
|
||||
|
@ -51,13 +57,15 @@ export class MePage extends Page {
|
|||
tag: "a",
|
||||
text: i18next.t("renew_lease_btn"),
|
||||
onclick: () => {
|
||||
renewSelf().then(() => {
|
||||
renewSelf()
|
||||
.then(() => {
|
||||
changePage("HOME");
|
||||
}).catch((e: Error) => {
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
setErrorText(e.message);
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "li",
|
||||
|
@ -75,8 +83,8 @@ export class MePage extends Page {
|
|||
onclick: async () => {
|
||||
await sealVault();
|
||||
changePage("UNSEAL_VAULT");
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "li",
|
||||
|
@ -85,11 +93,12 @@ export class MePage extends Page {
|
|||
text: i18next.t("change_language_btn"),
|
||||
onclick: () => {
|
||||
changePage("SET_LANGUAGE");
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
]
|
||||
}));
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
|
|
|
@ -4,40 +4,43 @@ import { Option } from "../elements/Option";
|
|||
import { Page } from "../types/Page";
|
||||
import { makeElement } from "../htmlUtils";
|
||||
import { setPageContent } from "../pageUtils";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
const passwordLengthMin = 1;
|
||||
const passwordLengthMax = 64;
|
||||
const passwordLengthDefault = 24;
|
||||
|
||||
function random() {
|
||||
if (typeof window.crypto?.getRandomValues === 'function' && typeof window.Uint32Array === 'function') {
|
||||
if (
|
||||
typeof window.crypto?.getRandomValues === "function" &&
|
||||
typeof window.Uint32Array === "function"
|
||||
) {
|
||||
return window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;
|
||||
}
|
||||
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
const lowerCase = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const upperCase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const numbers = '1234567890';
|
||||
const special = '!#$%&()*+,-./:;<=>?@[]^_{|}~';
|
||||
const lowerCase = "abcdefghijklmnopqrstuvwxyz";
|
||||
const upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const numbers = "1234567890";
|
||||
const special = "!#$%&()*+,-./:;<=>?@[]^_{|}~";
|
||||
|
||||
const alphabets = {
|
||||
SECURE: lowerCase + upperCase + numbers + special,
|
||||
SMOL: lowerCase + numbers,
|
||||
HEX: '123456789ABCDEF',
|
||||
}
|
||||
HEX: "123456789ABCDEF",
|
||||
};
|
||||
|
||||
const passwordOptionsDefault = {
|
||||
length: passwordLengthDefault,
|
||||
alphabet: alphabets.SECURE,
|
||||
}
|
||||
};
|
||||
|
||||
function genPassword(options = passwordOptionsDefault) {
|
||||
let pw = "";
|
||||
options = { ...passwordOptionsDefault, ...options }
|
||||
const pwArray = options.alphabet.split('');
|
||||
options = { ...passwordOptionsDefault, ...options };
|
||||
const pwArray = options.alphabet.split("");
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
pw = pw.concat(pwArray[Math.floor(random() * pwArray.length)]);
|
||||
}
|
||||
|
@ -57,11 +60,11 @@ export class PwGenPage extends Page {
|
|||
|
||||
render(): void {
|
||||
setPageContent("");
|
||||
this.passwordBox = CopyableInputBox(genPassword(passwordOptionsDefault)) ;
|
||||
this.passwordBox = CopyableInputBox(genPassword(passwordOptionsDefault));
|
||||
|
||||
this.passwordLengthTitle = makeElement({
|
||||
tag: "h4",
|
||||
text: this.getPasswordLengthText()
|
||||
text: this.getPasswordLengthText(),
|
||||
}) as HTMLTitleElement;
|
||||
|
||||
this.passwordLengthRange = makeElement({
|
||||
|
@ -76,7 +79,7 @@ export class PwGenPage extends Page {
|
|||
},
|
||||
}) as HTMLInputElement;
|
||||
|
||||
this.passwordLengthRange.addEventListener('input', this.updatePassword.bind(this));
|
||||
this.passwordLengthRange.addEventListener("input", this.updatePassword.bind(this));
|
||||
|
||||
this.passwordAlphabet = makeElement({
|
||||
tag: "select",
|
||||
|
@ -85,7 +88,7 @@ export class PwGenPage extends Page {
|
|||
Option("a-z a-Z 0-9 specials", alphabets.SECURE),
|
||||
Option("a-z 0-9", alphabets.SMOL),
|
||||
Option("A-F 1-9", alphabets.HEX),
|
||||
]
|
||||
],
|
||||
}) as HTMLSelectElement;
|
||||
|
||||
this.passwordForm = makeElement({
|
||||
|
@ -95,13 +98,15 @@ export class PwGenPage extends Page {
|
|||
Margin(this.passwordLengthRange),
|
||||
Margin(this.passwordAlphabet),
|
||||
Margin(this.passwordBox),
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
text: i18next.t("gen_password_btn"),
|
||||
class: ["uk-button", "uk-button-primary", "uk-margin-bottom"],
|
||||
attributes: { type: "submit" },
|
||||
}))
|
||||
]
|
||||
}),
|
||||
),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
|
||||
this.passwordForm.addEventListener("submit", (e) => this.formEvent(e));
|
||||
|
@ -111,7 +116,7 @@ export class PwGenPage extends Page {
|
|||
getPasswordLengthText(): string {
|
||||
return i18next.t("password_length_title", {
|
||||
min: this?.passwordLengthRange?.value || 24,
|
||||
max: passwordLengthMax
|
||||
max: passwordLengthMax,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -122,10 +127,12 @@ export class PwGenPage extends Page {
|
|||
|
||||
updatePassword(): void {
|
||||
this.passwordLengthTitle.innerText = this.getPasswordLengthText();
|
||||
this.passwordBox.setText(genPassword({
|
||||
length: (this.passwordLengthRange.value as unknown) as number,
|
||||
alphabet: this.passwordAlphabet.value ,
|
||||
}));
|
||||
this.passwordBox.setText(
|
||||
genPassword({
|
||||
length: this.passwordLengthRange.value as unknown as number,
|
||||
alphabet: this.passwordAlphabet.value,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
cleanup(): void {
|
||||
|
|
|
@ -3,8 +3,8 @@ import { Page } from "../types/Page";
|
|||
import { changePage, setPageContent } from "../pageUtils";
|
||||
import { makeElement } from "../htmlUtils";
|
||||
import { pageState } from "../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import { reloadNavBar } from "../elements/NavBar";
|
||||
import i18next from "i18next";
|
||||
|
||||
// @ts-ignore
|
||||
import translations from "../translations/index.mjs";
|
||||
|
@ -20,25 +20,26 @@ export class SetLanguagePage extends Page {
|
|||
tag: "form",
|
||||
id: "setLanguageForm",
|
||||
children: [
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "select",
|
||||
class: ["uk-select", "uk-form-width-large"],
|
||||
attributes: {
|
||||
name: "language"
|
||||
name: "language",
|
||||
},
|
||||
children: languageIDs.map(function (languageID) {
|
||||
return makeElement({
|
||||
tag: "option",
|
||||
text: i18next.getFixedT(languageID, null)("language_name"),
|
||||
attributes: { value: languageID }
|
||||
})
|
||||
})
|
||||
})
|
||||
attributes: { value: languageID },
|
||||
});
|
||||
}),
|
||||
}),
|
||||
),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
|
@ -46,9 +47,9 @@ export class SetLanguagePage extends Page {
|
|||
text: i18next.t("set_language_btn"),
|
||||
attributes: {
|
||||
type: "submit",
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
}),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
setPageContent(setLanguageForm);
|
||||
setLanguageForm.addEventListener("submit", function (e) {
|
||||
|
|
|
@ -8,7 +8,8 @@ export class SetVaultURLPage extends Page {
|
|||
super();
|
||||
}
|
||||
render(): void {
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "form",
|
||||
id: "setVaultURLForm",
|
||||
children: [
|
||||
|
@ -22,14 +23,14 @@ export class SetVaultURLPage extends Page {
|
|||
required: "true",
|
||||
type: "text",
|
||||
placeholder: "Vault URL",
|
||||
name: "vaultURL"
|
||||
}
|
||||
})
|
||||
name: "vaultURL",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
|
@ -37,13 +38,14 @@ export class SetVaultURLPage extends Page {
|
|||
text: "Set",
|
||||
attributes: {
|
||||
type: "submit",
|
||||
}
|
||||
})
|
||||
]
|
||||
}));
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
document.getElementById("setVaultURLForm").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(document.querySelector('#setVaultURLForm'));
|
||||
const formData = new FormData(document.querySelector("#setVaultURLForm"));
|
||||
pageState.apiURL = formData.get("vaultURL") as string;
|
||||
changePage("HOME");
|
||||
});
|
||||
|
|
|
@ -5,10 +5,10 @@ import { addNewTOTP } from "../../api/totp/addNewTOTP";
|
|||
import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils";
|
||||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
function replaceAll(str: string, replace: string, replaceWith: string): string {
|
||||
return str.replace(new RegExp(replace, 'g'), replaceWith);
|
||||
return str.replace(new RegExp(replace, "g"), replaceWith);
|
||||
}
|
||||
function removeDashSpaces(str: string): string {
|
||||
str = replaceAll(str, "-", "");
|
||||
|
@ -29,52 +29,60 @@ export class NewTOTPPage extends Page {
|
|||
const totpForm = makeElement({
|
||||
tag: "form",
|
||||
children: [
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "input",
|
||||
class: ["uk-input", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
required: "true",
|
||||
type: "text",
|
||||
placeholder: i18next.t("totp_new_name_text"),
|
||||
name: "name"
|
||||
}
|
||||
})),
|
||||
name: "name",
|
||||
},
|
||||
}),
|
||||
),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("totp_new_info")
|
||||
text: i18next.t("totp_new_info"),
|
||||
}),
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "input",
|
||||
class: ["uk-input", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
type: "text",
|
||||
placeholder: i18next.t("totp_new_uri_input"),
|
||||
name: "uri"
|
||||
}
|
||||
})),
|
||||
Margin(makeElement({
|
||||
name: "uri",
|
||||
},
|
||||
}),
|
||||
),
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "input",
|
||||
class: ["uk-input", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
type: "text",
|
||||
placeholder: i18next.t("totp_new_key_input"),
|
||||
name: "key"
|
||||
}
|
||||
})),
|
||||
name: "key",
|
||||
},
|
||||
}),
|
||||
),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
MarginInline(makeElement({
|
||||
MarginInline(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
text: i18next.t("totp_new_add_btn"),
|
||||
attributes: {
|
||||
type: "submit"
|
||||
}
|
||||
}))
|
||||
]
|
||||
type: "submit",
|
||||
},
|
||||
}),
|
||||
),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
setPageContent(totpForm);
|
||||
|
||||
|
@ -85,11 +93,13 @@ export class NewTOTPPage extends Page {
|
|||
url: formData.get("uri") as string,
|
||||
key: removeDashSpaces(formData.get("key") as string).toUpperCase(),
|
||||
name: formData.get("name") as string,
|
||||
generate: false
|
||||
generate: false,
|
||||
};
|
||||
addNewTOTP(pageState.currentBaseMount, parms).then(_ => {
|
||||
addNewTOTP(pageState.currentBaseMount, parms)
|
||||
.then((_) => {
|
||||
changePage("TOTP");
|
||||
}).catch((e: Error) => {
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
setErrorText(`API Error: ${e.message}`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,13 +7,12 @@ import { getTOTPKeys } from "../../api/totp/getTOTPKeys";
|
|||
import { makeElement } from "../../htmlUtils";
|
||||
import { objectToMap } from "../../utils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export interface TOTPListElement extends HTMLElement {
|
||||
setCode(code: string): void;
|
||||
}
|
||||
|
||||
|
||||
export class TOTPViewPage extends Page {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -27,13 +26,16 @@ export class TOTPViewPage extends Page {
|
|||
async render(): Promise<void> {
|
||||
setTitleElement(pageState);
|
||||
const totpList = makeElement({ tag: "div" });
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
children: [
|
||||
makeElement({
|
||||
tag: "a",
|
||||
text: i18next.t("totp_view_new_btn"),
|
||||
onclick: () => { changePage("NEW_TOTP"); }
|
||||
onclick: () => {
|
||||
changePage("NEW_TOTP");
|
||||
},
|
||||
}),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
|
@ -42,10 +44,10 @@ export class TOTPViewPage extends Page {
|
|||
}),
|
||||
makeElement({ tag: "br" }),
|
||||
makeElement({ tag: "br" }),
|
||||
totpList
|
||||
]
|
||||
}));
|
||||
|
||||
totpList,
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
try {
|
||||
const res = await getTOTPKeys(pageState.currentBaseMount);
|
||||
|
@ -66,14 +68,17 @@ export class TOTPViewPage extends Page {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const totpRefresher = async () => {
|
||||
await Promise.all(Array.from(objectToMap(this.totpListElements)).map((kv: [string, TOTPListElement]) => {
|
||||
await Promise.all(
|
||||
Array.from(objectToMap(this.totpListElements)).map((kv: [string, TOTPListElement]) => {
|
||||
return this.updateTOTPElement(...kv);
|
||||
}))
|
||||
}
|
||||
}),
|
||||
);
|
||||
};
|
||||
await totpRefresher();
|
||||
this.refresher = setInterval(() => { void totpRefresher(); }, 3000) as unknown as number;
|
||||
this.refresher = setInterval(() => {
|
||||
void totpRefresher();
|
||||
}, 3000) as unknown as number;
|
||||
}
|
||||
|
||||
cleanup(): void {
|
||||
|
@ -92,7 +97,7 @@ export class TOTPViewPage extends Page {
|
|||
const gridElement = makeElement({
|
||||
tag: "div",
|
||||
class: ["uk-grid", "uk-grid-small", "uk-text-expand"],
|
||||
children: [totpKeyBox, totpValueBox]
|
||||
children: [totpKeyBox, totpValueBox],
|
||||
}) as TOTPListElement;
|
||||
|
||||
gridElement.setCode = (code: string) => totpValueBox.setText(code);
|
||||
|
|
|
@ -21,20 +21,24 @@ export class TransitDecryptPage extends Page {
|
|||
|
||||
render(): void {
|
||||
setTitleElement(pageState);
|
||||
setPageContent(makeElement({
|
||||
tag: "div"
|
||||
}));
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
}),
|
||||
);
|
||||
this.transitDecryptForm = makeElement({
|
||||
tag: "form",
|
||||
children: [
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "textarea",
|
||||
class: ["uk-textarea", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
placeholder: i18next.t("transit_decrypt_input_placeholder"),
|
||||
name: "ciphertext",
|
||||
}
|
||||
})),
|
||||
},
|
||||
}),
|
||||
),
|
||||
Margin(FileUploadInput("ciphertext_file")),
|
||||
Margin([
|
||||
makeElement({
|
||||
|
@ -51,14 +55,14 @@ export class TransitDecryptPage extends Page {
|
|||
attributes: {
|
||||
type: "checkbox",
|
||||
name: "decodeBase64Checkbox",
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
|
@ -66,9 +70,9 @@ export class TransitDecryptPage extends Page {
|
|||
text: i18next.t("transit_decrypt_decrypt_btn"),
|
||||
attributes: {
|
||||
type: "submit",
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
}),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
setPageContent(this.transitDecryptForm);
|
||||
this.transitDecryptForm.addEventListener("submit", (e: Event) => {
|
||||
|
@ -86,20 +90,23 @@ export class TransitDecryptPage extends Page {
|
|||
|
||||
const ciphertext_file = formData.get("ciphertext_file") as File;
|
||||
if (ciphertext_file.size > 0) {
|
||||
ciphertext = atob((await fileToBase64(ciphertext_file) ).replace("data:text/plain;base64,", ""));
|
||||
ciphertext = atob(
|
||||
(await fileToBase64(ciphertext_file)).replace("data:text/plain;base64,", ""),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await transitDecrypt(
|
||||
pageState.currentBaseMount,
|
||||
pageState.currentSecret,
|
||||
{ ciphertext: ciphertext },
|
||||
);
|
||||
const res = await transitDecrypt(pageState.currentBaseMount, pageState.currentSecret, {
|
||||
ciphertext: ciphertext,
|
||||
});
|
||||
let plaintext = res.plaintext;
|
||||
if (decodeBase64 == "on") {
|
||||
plaintext = atob(plaintext);
|
||||
}
|
||||
const modal = CopyableModal(i18next.t("transit_decrypt_decryption_result_modal_title"), plaintext);
|
||||
const modal = CopyableModal(
|
||||
i18next.t("transit_decrypt_decryption_result_modal_title"),
|
||||
plaintext,
|
||||
);
|
||||
document.body.querySelector("#pageContent").appendChild(modal);
|
||||
modal.show();
|
||||
} catch (e: unknown) {
|
||||
|
|
|
@ -8,7 +8,6 @@ import { pageState } from "../../globalPageState";
|
|||
import { transitEncrypt } from "../../api/transit/transitEncrypt";
|
||||
import i18next from "i18next";
|
||||
|
||||
|
||||
export class TransitEncryptPage extends Page {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -20,23 +19,26 @@ export class TransitEncryptPage extends Page {
|
|||
|
||||
transitEncryptForm: HTMLFormElement;
|
||||
|
||||
|
||||
render(): void {
|
||||
setTitleElement(pageState);
|
||||
setPageContent(makeElement({
|
||||
tag: "div"
|
||||
}));
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
}),
|
||||
);
|
||||
this.transitEncryptForm = makeElement({
|
||||
tag: "form",
|
||||
children: [
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "textarea",
|
||||
class: ["uk-textarea", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
placeholder: i18next.t("transit_encrypt_input_placeholder"),
|
||||
name: "plaintext",
|
||||
}
|
||||
})),
|
||||
},
|
||||
}),
|
||||
),
|
||||
Margin(FileUploadInput("plaintext_file")),
|
||||
Margin([
|
||||
makeElement({
|
||||
|
@ -53,14 +55,14 @@ export class TransitEncryptPage extends Page {
|
|||
attributes: {
|
||||
type: "checkbox",
|
||||
name: "base64Checkbox",
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
|
@ -68,9 +70,9 @@ export class TransitEncryptPage extends Page {
|
|||
text: i18next.t("transit_encrypt_encrypt_btn"),
|
||||
attributes: {
|
||||
type: "submit",
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
}),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
setPageContent(this.transitEncryptForm);
|
||||
|
||||
|
@ -89,19 +91,20 @@ export class TransitEncryptPage extends Page {
|
|||
|
||||
const plaintext_file = formData.get("plaintext_file") as File;
|
||||
if (plaintext_file.size > 0) {
|
||||
plaintext = (await fileToBase64(plaintext_file) ).replace("data:text/plain;base64,", "");
|
||||
plaintext = (await fileToBase64(plaintext_file)).replace("data:text/plain;base64,", "");
|
||||
plaintext = base64Checkbox == "on" ? atob(plaintext) : plaintext;
|
||||
} else {
|
||||
plaintext = base64Checkbox == "on" ? plaintext : btoa(plaintext);
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await transitEncrypt(
|
||||
pageState.currentBaseMount,
|
||||
pageState.currentSecret,
|
||||
{ plaintext: plaintext }
|
||||
const res = await transitEncrypt(pageState.currentBaseMount, pageState.currentSecret, {
|
||||
plaintext: plaintext,
|
||||
});
|
||||
const modal = CopyableModal(
|
||||
i18next.t("transit_encrypt_encryption_result_modal_title"),
|
||||
res.ciphertext,
|
||||
);
|
||||
const modal = CopyableModal(i18next.t("transit_encrypt_encryption_result_modal_title"), res.ciphertext);
|
||||
document.body.querySelector("#pageContent").appendChild(modal);
|
||||
modal.show();
|
||||
} catch (e: unknown) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { pageState } from "../../globalPageState";
|
|||
import { transitRewrap } from "../../api/transit/transitRewrap";
|
||||
import i18next from "i18next";
|
||||
|
||||
type versionOption = { version: string; label: string }
|
||||
type versionOption = { version: string; label: string };
|
||||
|
||||
export class TransitRewrapPage extends Page {
|
||||
constructor() {
|
||||
|
@ -27,7 +27,9 @@ export class TransitRewrapPage extends Page {
|
|||
setTitleElement(pageState);
|
||||
const transitKey = await getTransitKey(pageState.currentBaseMount, pageState.currentSecret);
|
||||
|
||||
const stringVersions = Array.from(objectToMap(transitKey.keys).keys()).reverse() as unknown as string[];
|
||||
const stringVersions = Array.from(
|
||||
objectToMap(transitKey.keys).keys(),
|
||||
).reverse() as unknown as string[];
|
||||
const versions = stringVersions.map((val): number => parseInt(val, 10));
|
||||
|
||||
// get the selectable version options in the same
|
||||
|
@ -35,15 +37,15 @@ export class TransitRewrapPage extends Page {
|
|||
// e.g: ["2 (latest)", "1"]
|
||||
|
||||
const options: versionOption[] = versions.map((val): versionOption => {
|
||||
const i18nkey = val == Math.max(...versions) ?
|
||||
"transit_rewrap_latest_version_option_text"
|
||||
:
|
||||
"transit_rewrap_version_option_text";
|
||||
const i18nkey =
|
||||
val == Math.max(...versions)
|
||||
? "transit_rewrap_latest_version_option_text"
|
||||
: "transit_rewrap_version_option_text";
|
||||
return {
|
||||
version: String(val),
|
||||
label: i18next.t(i18nkey, { version_num: String(val) }),
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
setPageContent("");
|
||||
this.transitRewrapForm = makeElement({
|
||||
|
@ -53,20 +55,22 @@ export class TransitRewrapPage extends Page {
|
|||
tag: "select",
|
||||
name: "version",
|
||||
class: ["uk-select", "uk-width-1-2"],
|
||||
children: options.map((option): HTMLElement => Option(option.label, option.version))
|
||||
children: options.map((option): HTMLElement => Option(option.label, option.version)),
|
||||
}),
|
||||
Margin(makeElement({
|
||||
Margin(
|
||||
makeElement({
|
||||
tag: "textarea",
|
||||
class: ["uk-textarea", "uk-width-1-2"],
|
||||
attributes: {
|
||||
placeholder: i18next.t("transit_rewrap_input_placeholder"),
|
||||
name: "ciphertext",
|
||||
}
|
||||
})),
|
||||
},
|
||||
}),
|
||||
),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
class: "uk-text-danger",
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
|
@ -74,9 +78,9 @@ export class TransitRewrapPage extends Page {
|
|||
text: i18next.t("transit_rewrap_rewrap_btn"),
|
||||
attributes: {
|
||||
type: "submit",
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
}),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
setPageContent(this.transitRewrapForm);
|
||||
|
||||
|
@ -89,14 +93,10 @@ export class TransitRewrapPage extends Page {
|
|||
async transitRewrapFormHandler(): Promise<void> {
|
||||
const formData = new FormData(this.transitRewrapForm);
|
||||
try {
|
||||
const res = await transitRewrap(
|
||||
pageState.currentBaseMount,
|
||||
pageState.currentSecret,
|
||||
{
|
||||
const res = await transitRewrap(pageState.currentBaseMount, pageState.currentSecret, {
|
||||
ciphertext: formData.get("ciphertext") as string,
|
||||
key_version: parseInt(formData.get("version") as string, 10),
|
||||
}
|
||||
);
|
||||
});
|
||||
const modal = CopyableModal(i18next.t("transit_rewrap_result_modal_title"), res.ciphertext);
|
||||
document.body.querySelector("#pageContent").appendChild(modal);
|
||||
modal.show();
|
||||
|
|
|
@ -4,7 +4,7 @@ import { changePage, setErrorText, setPageContent, setTitleElement } from "../..
|
|||
import { getTransitKeys } from "../../api/transit/getTransitKeys";
|
||||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export class TransitViewPage extends Page {
|
||||
constructor() {
|
||||
|
@ -29,14 +29,15 @@ export class TransitViewPage extends Page {
|
|||
class: ["uk-button", "uk-button-primary", "uk-margin-bottom"],
|
||||
onclick: () => {
|
||||
changePage("TRANSIT_NEW_KEY");
|
||||
}
|
||||
},
|
||||
});
|
||||
transitViewContent.appendChild(newButton);
|
||||
|
||||
try {
|
||||
const res = await getTransitKeys(pageState.currentBaseMount);
|
||||
|
||||
transitViewContent.appendChild(makeElement({
|
||||
transitViewContent.appendChild(
|
||||
makeElement({
|
||||
tag: "ul",
|
||||
class: ["uk-nav", "uk-nav-default"],
|
||||
children: [
|
||||
|
@ -49,19 +50,22 @@ export class TransitViewPage extends Page {
|
|||
onclick: () => {
|
||||
pageState.currentSecret = secret;
|
||||
changePage("TRANSIT_VIEW_SECRET");
|
||||
}
|
||||
})
|
||||
},
|
||||
}),
|
||||
});
|
||||
})
|
||||
]
|
||||
}));
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
const error = e as Error;
|
||||
if (error == DoesNotExistError) {
|
||||
transitViewContent.appendChild(makeElement({
|
||||
transitViewContent.appendChild(
|
||||
makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("transit_view_none_here_text")
|
||||
}));
|
||||
text: i18next.t("transit_view_none_here_text"),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
setErrorText(error.message);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { changePage, setPageContent, setTitleElement } from "../../pageUtils";
|
|||
import { getTransitKey } from "../../api/transit/getTransitKey";
|
||||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
export class TransitViewSecretPage extends Page {
|
||||
constructor() {
|
||||
|
@ -20,7 +20,8 @@ export class TransitViewSecretPage extends Page {
|
|||
|
||||
const transitKey = await getTransitKey(pageState.currentBaseMount, pageState.currentSecret);
|
||||
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
class: "uk-child-width-1-1@s uk-child-width-1-2@m uk-grid-small uk-grid-match",
|
||||
attributes: { "uk-grid": "" },
|
||||
|
@ -31,7 +32,9 @@ export class TransitViewSecretPage extends Page {
|
|||
description: i18next.t("transit_view_encrypt_description"),
|
||||
icon: "lock",
|
||||
iconText: i18next.t("transit_view_encrypt_icon_text"),
|
||||
onclick: () => { changePage("TRANSIT_ENCRYPT"); }
|
||||
onclick: () => {
|
||||
changePage("TRANSIT_ENCRYPT");
|
||||
},
|
||||
}),
|
||||
Tile({
|
||||
condition: transitKey.supports_decryption,
|
||||
|
@ -39,7 +42,9 @@ export class TransitViewSecretPage extends Page {
|
|||
description: i18next.t("transit_view_decrypt_description"),
|
||||
icon: "mail",
|
||||
iconText: i18next.t("transit_view_decrypt_icon_text"),
|
||||
onclick: () => { changePage("TRANSIT_DECRYPT"); }
|
||||
onclick: () => {
|
||||
changePage("TRANSIT_DECRYPT");
|
||||
},
|
||||
}),
|
||||
Tile({
|
||||
condition: transitKey.supports_decryption,
|
||||
|
@ -47,10 +52,13 @@ export class TransitViewSecretPage extends Page {
|
|||
description: i18next.t("transit_view_rewrap_description"),
|
||||
icon: "code",
|
||||
iconText: i18next.t("transit_view_rewrap_icon_text"),
|
||||
onclick: () => { changePage("TRANSIT_REWRAP"); }
|
||||
onclick: () => {
|
||||
changePage("TRANSIT_REWRAP");
|
||||
},
|
||||
}),
|
||||
]
|
||||
}));
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
|
|
|
@ -5,13 +5,12 @@ import { SealStatusType, getSealStatus } from "../api/sys/getSealStatus";
|
|||
import { changePage, setErrorText, setPageContent } from "../pageUtils";
|
||||
import { makeElement } from "../htmlUtils";
|
||||
import { submitUnsealKey } from "../api/sys/submitUnsealKey";
|
||||
import i18next from 'i18next';
|
||||
|
||||
import i18next from "i18next";
|
||||
|
||||
const UnsealInputModes = {
|
||||
FORM_INPUT: "FORM_INPUT",
|
||||
QR_INPUT: "QR_INPUT"
|
||||
}
|
||||
QR_INPUT: "QR_INPUT",
|
||||
};
|
||||
|
||||
export class UnsealPage extends Page {
|
||||
constructor() {
|
||||
|
@ -57,28 +56,33 @@ export class UnsealPage extends Page {
|
|||
this.unsealProgress = makeElement({
|
||||
tag: "progress",
|
||||
class: "uk-progress",
|
||||
attributes: { value: "0", max: "0" }
|
||||
attributes: { value: "0", max: "0" },
|
||||
}) as HTMLProgressElement;
|
||||
this.unsealProgressText = makeElement({
|
||||
tag: "p",
|
||||
text: i18next.t("unseal_keys_progress", { progress: "0", keys_needed: "0" }),
|
||||
text: i18next.t("unseal_keys_progress", {
|
||||
progress: "0",
|
||||
keys_needed: "0",
|
||||
}),
|
||||
}) as HTMLParagraphElement;
|
||||
this.unsealInputContent = makeElement({
|
||||
tag: "div"
|
||||
tag: "div",
|
||||
});
|
||||
setPageContent(makeElement({
|
||||
setPageContent(
|
||||
makeElement({
|
||||
tag: "div",
|
||||
children: [
|
||||
this.unsealProgress,
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: ["uk-text-danger", "uk-margin-top"]
|
||||
class: ["uk-text-danger", "uk-margin-top"],
|
||||
}),
|
||||
this.unsealProgressText,
|
||||
this.unsealInputContent
|
||||
]
|
||||
}));
|
||||
this.unsealInputContent,
|
||||
],
|
||||
}),
|
||||
);
|
||||
this.switchInputMode(this.mode);
|
||||
this.updateSealProgress(await getSealStatus());
|
||||
this.makeRefresher();
|
||||
|
@ -86,29 +90,28 @@ export class UnsealPage extends Page {
|
|||
|
||||
setButtons(method: string): void {
|
||||
const newMethod: string =
|
||||
method == UnsealInputModes.FORM_INPUT ?
|
||||
UnsealInputModes.QR_INPUT
|
||||
:
|
||||
UnsealInputModes.FORM_INPUT;
|
||||
method == UnsealInputModes.FORM_INPUT
|
||||
? UnsealInputModes.QR_INPUT
|
||||
: UnsealInputModes.FORM_INPUT;
|
||||
const buttonText: string =
|
||||
newMethod == UnsealInputModes.FORM_INPUT ?
|
||||
i18next.t("unseal_input_btn")
|
||||
:
|
||||
i18next.t("unseal_qr_btn");
|
||||
this.unsealInputContent.appendChild(makeElement({
|
||||
newMethod == UnsealInputModes.FORM_INPUT
|
||||
? i18next.t("unseal_input_btn")
|
||||
: i18next.t("unseal_qr_btn");
|
||||
this.unsealInputContent.appendChild(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
text: buttonText,
|
||||
onclick: () => {
|
||||
this.switchInputMode(newMethod);
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
switchInputMode(method: string): void {
|
||||
this.deinitWebcam();
|
||||
this.unsealInputContent.querySelectorAll('*').forEach(n => n.remove())
|
||||
this.unsealInputContent.querySelectorAll("*").forEach((n) => n.remove());
|
||||
if (method == UnsealInputModes.FORM_INPUT) this.makeUnsealForm();
|
||||
if (method == UnsealInputModes.QR_INPUT) void this.makeQRInput();
|
||||
this.setButtons(method);
|
||||
|
@ -118,22 +121,26 @@ export class UnsealPage extends Page {
|
|||
this.unsealKeyForm = makeElement({
|
||||
tag: "form",
|
||||
children: [
|
||||
MarginInline(makeElement({
|
||||
MarginInline(
|
||||
makeElement({
|
||||
tag: "input",
|
||||
class: ["uk-input", "uk-form-width-medium"],
|
||||
attributes: {
|
||||
required: "true",
|
||||
type: "password",
|
||||
placeholder: i18next.t("key_input_placeholder"),
|
||||
name: "key"
|
||||
}
|
||||
})),
|
||||
MarginInline(makeElement({
|
||||
name: "key",
|
||||
},
|
||||
}),
|
||||
),
|
||||
MarginInline(
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
text: i18next.t("submit_key_btn")
|
||||
})),
|
||||
]
|
||||
text: i18next.t("submit_key_btn"),
|
||||
}),
|
||||
),
|
||||
],
|
||||
}) as HTMLFormElement;
|
||||
this.unsealInputContent.appendChild(this.unsealKeyForm);
|
||||
this.unsealKeyForm.addEventListener("submit", (e: Event) => {
|
||||
|
@ -155,7 +162,7 @@ export class UnsealPage extends Page {
|
|||
const text = this.unsealProgressText;
|
||||
text.innerText = i18next.t("unseal_keys_progress", {
|
||||
progress: String(progress),
|
||||
keys_needed: String(keysNeeded)
|
||||
keys_needed: String(keysNeeded),
|
||||
});
|
||||
const progressBar = this.unsealProgress;
|
||||
progressBar.value = progress;
|
||||
|
@ -167,11 +174,13 @@ export class UnsealPage extends Page {
|
|||
}
|
||||
|
||||
submitKey(key: string): void {
|
||||
submitUnsealKey(key).then(_ => {
|
||||
void getSealStatus().then(data => {
|
||||
submitUnsealKey(key)
|
||||
.then((_) => {
|
||||
void getSealStatus().then((data) => {
|
||||
void this.updateSealProgress(data);
|
||||
});
|
||||
}).catch((e: Error) => {
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
setErrorText(e.message);
|
||||
});
|
||||
}
|
||||
|
@ -179,7 +188,7 @@ export class UnsealPage extends Page {
|
|||
handleKeySubmit(): void {
|
||||
const formData = new FormData(this.unsealKeyForm);
|
||||
|
||||
this.submitKey(formData.get("key") as string)
|
||||
this.submitKey(formData.get("key") as string);
|
||||
}
|
||||
get name(): string {
|
||||
return i18next.t("unseal_vault_text");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PageState } from "./PageState";
|
||||
import { pageState } from "./globalPageState";
|
||||
import i18next from 'i18next';
|
||||
import i18next from "i18next";
|
||||
|
||||
// Playground is a way to debug and test things.
|
||||
// Anything you put in here is gonna be run on page initial load
|
||||
|
@ -8,7 +8,10 @@ import i18next from 'i18next';
|
|||
// Also it only runs when process.env.NODE_ENV == "development"
|
||||
|
||||
declare global {
|
||||
interface Window { pageState: PageState; i18next: unknown; }
|
||||
interface Window {
|
||||
pageState: PageState;
|
||||
i18next: unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Please empty this function before committing.
|
||||
|
|
31
src/translations/de.js
vendored
31
src/translations/de.js
vendored
|
@ -1,25 +1,24 @@
|
|||
module.exports = {
|
||||
// The localised name for the language
|
||||
"language_name": "Deutsche",
|
||||
language_name: "Deutsche",
|
||||
// Internal: The direction of text (ltr or rtl)
|
||||
"language_direction": "ltr",
|
||||
language_direction: "ltr",
|
||||
|
||||
// These are the buttons on the top bar.
|
||||
"home_btn": "Startseite",
|
||||
"back_btn": "Zurück",
|
||||
"refresh_btn": "Neu laden",
|
||||
"me_btn": "Profil/Einstellungen",
|
||||
home_btn: "Startseite",
|
||||
back_btn: "Zurück",
|
||||
refresh_btn: "Neu laden",
|
||||
me_btn: "Profil/Einstellungen",
|
||||
// These are the page titles
|
||||
"me_page_title": "Proil/Einstellungen",
|
||||
"home_page_title": "Startseite",
|
||||
me_page_title: "Proil/Einstellungen",
|
||||
home_page_title: "Startseite",
|
||||
|
||||
// These are all o the other translations
|
||||
"log_out_btn": "Abmelden",
|
||||
"copy_token_btn": "Token kopieren",
|
||||
"renew_lease_btn": "Token erneuern",
|
||||
log_out_btn: "Abmelden",
|
||||
copy_token_btn: "Token kopieren",
|
||||
renew_lease_btn: "Token erneuern",
|
||||
|
||||
"vaulturl_text": "Tresor-URL: {{text}}",
|
||||
"password_generator_btn": "Passwortgenerator",
|
||||
"your_token_expires_in": "Ihr Token läuft in {{date, until_date}} ab"
|
||||
|
||||
}
|
||||
vaulturl_text: "Tresor-URL: {{text}}",
|
||||
password_generator_btn: "Passwortgenerator",
|
||||
your_token_expires_in: "Ihr Token läuft in {{date, until_date}} ab",
|
||||
};
|
||||
|
|
230
src/translations/en.js
vendored
230
src/translations/en.js
vendored
|
@ -1,164 +1,168 @@
|
|||
module.exports = {
|
||||
// The localised name for the language
|
||||
"language_name": "English",
|
||||
language_name: "English",
|
||||
// Internal: The direction of text (ltr or rtl)
|
||||
"language_direction": "ltr",
|
||||
language_direction: "ltr",
|
||||
|
||||
// These are the buttons on the top bar.
|
||||
"home_btn": "Home",
|
||||
"back_btn": "Back",
|
||||
"refresh_btn": "Refresh",
|
||||
"me_btn": "Me/Settings",
|
||||
home_btn: "Home",
|
||||
back_btn: "Back",
|
||||
refresh_btn: "Refresh",
|
||||
me_btn: "Me/Settings",
|
||||
|
||||
// General Notification Messages
|
||||
"notification_copy_success": "Copied to clipboard.",
|
||||
notification_copy_success: "Copied to clipboard.",
|
||||
|
||||
// Copyable Modal
|
||||
"copy_modal_download_btn": "Download",
|
||||
"copy_modal_copy_btn": "Copy",
|
||||
"copy_modal_close_btn": "Close",
|
||||
copy_modal_download_btn: "Download",
|
||||
copy_modal_copy_btn: "Copy",
|
||||
copy_modal_close_btn: "Close",
|
||||
|
||||
// Copyable Input Box
|
||||
"copy_input_box_copy_icon_text": "Copy Button",
|
||||
copy_input_box_copy_icon_text: "Copy Button",
|
||||
|
||||
// File Upload Input
|
||||
"file_upload_input_btn": "Upload File",
|
||||
file_upload_input_btn: "Upload File",
|
||||
|
||||
// Me Page
|
||||
"me_page_title": "Me/Settings",
|
||||
"log_out_btn": "Log Out",
|
||||
"seal_vault_btn": "Seal Vault",
|
||||
"copy_token_btn": "Copy Token",
|
||||
"renew_lease_btn": "Renew Token Lease",
|
||||
"change_language_btn": "Change Language",
|
||||
me_page_title: "Me/Settings",
|
||||
log_out_btn: "Log Out",
|
||||
seal_vault_btn: "Seal Vault",
|
||||
copy_token_btn: "Copy Token",
|
||||
renew_lease_btn: "Renew Token Lease",
|
||||
change_language_btn: "Change Language",
|
||||
|
||||
// Home Page
|
||||
"home_page_title": "Home",
|
||||
"vaulturl_text": "Vault URL: {{text}}",
|
||||
"password_generator_btn": "Password Generator",
|
||||
"your_token_expires_in": "Your token expires in {{date, until_date}}",
|
||||
home_page_title: "Home",
|
||||
vaulturl_text: "Vault URL: {{text}}",
|
||||
password_generator_btn: "Password Generator",
|
||||
your_token_expires_in: "Your token expires in {{date, until_date}}",
|
||||
|
||||
// Unseal Page
|
||||
"unseal_vault_text": "Unseal the Vault",
|
||||
"submit_key_btn": "Submit Key",
|
||||
"unseal_input_btn": "Switch to Manual Key Input",
|
||||
"unseal_qr_btn": "Switch to QR Key Input",
|
||||
"key_input_placeholder": "Key",
|
||||
"unseal_keys_progress": "Keys: {{progress}}/{{keys_needed}}",
|
||||
unseal_vault_text: "Unseal the Vault",
|
||||
submit_key_btn: "Submit Key",
|
||||
unseal_input_btn: "Switch to Manual Key Input",
|
||||
unseal_qr_btn: "Switch to QR Key Input",
|
||||
key_input_placeholder: "Key",
|
||||
unseal_keys_progress: "Keys: {{progress}}/{{keys_needed}}",
|
||||
|
||||
// Language Selector Page
|
||||
"set_language_title": "Set Language",
|
||||
"set_language_btn": "Set Language",
|
||||
set_language_title: "Set Language",
|
||||
set_language_btn: "Set Language",
|
||||
|
||||
// Password Generator Page
|
||||
"password_generator_title": "Password Generator",
|
||||
"password_length_title": "Password Length ({{min}}/{{max}})",
|
||||
"gen_password_btn": "Generate Password",
|
||||
password_generator_title: "Password Generator",
|
||||
password_length_title: "Password Length ({{min}}/{{max}})",
|
||||
gen_password_btn: "Generate Password",
|
||||
|
||||
// Login Page
|
||||
"log_in_title": "Login",
|
||||
"log_in_with_token": "Token",
|
||||
"log_in_with_username": "Username",
|
||||
"token_input": "Token",
|
||||
"username_input": "Username",
|
||||
"password_input": "Password",
|
||||
"log_in_btn": "Login",
|
||||
"token_login_error": "Invalid Token",
|
||||
log_in_title: "Login",
|
||||
log_in_with_token: "Token",
|
||||
log_in_with_username: "Username",
|
||||
token_input: "Token",
|
||||
username_input: "Username",
|
||||
password_input: "Password",
|
||||
log_in_btn: "Login",
|
||||
token_login_error: "Invalid Token",
|
||||
|
||||
// Key Value Delete Page
|
||||
"kv_delete_title": "K/V Delete",
|
||||
"kv_delete_text": "Are you sure you want to delete this?",
|
||||
"kv_delete_btn": "Delete",
|
||||
"kv_delete_suffix": " (delete)",
|
||||
kv_delete_title: "K/V Delete",
|
||||
kv_delete_text: "Are you sure you want to delete this?",
|
||||
kv_delete_btn: "Delete",
|
||||
kv_delete_suffix: " (delete)",
|
||||
|
||||
// Key Value New Page
|
||||
"kv_new_title": "K/V New",
|
||||
"kv_new_suffix": " (new)",
|
||||
"kv_new_path": "Relative Path",
|
||||
"kv_new_create_btn": "Create Empty Secret",
|
||||
kv_new_title: "K/V New",
|
||||
kv_new_suffix: " (new)",
|
||||
kv_new_path: "Relative Path",
|
||||
kv_new_create_btn: "Create Empty Secret",
|
||||
|
||||
// Key Value Secret Page
|
||||
"kv_secret_title": "K/V Secret",
|
||||
"kv_secret_deleted_text": "This secret version has been soft deleted but remains restorable, do you want to restore it?",
|
||||
"kv_secret_restore_btn": "Restore Secret Version",
|
||||
"kv_secret_loading": "Loading Secret..",
|
||||
"kv_secret_delete_btn": "Delete",
|
||||
"kv_secret_delete_all_btn": "Delete All Versions",
|
||||
"kv_secret_delete_version_btn": "Delete Version {{ version }}",
|
||||
"kv_secret_edit_btn": "Edit",
|
||||
"kv_secret_versions_btn": "Versions",
|
||||
kv_secret_title: "K/V Secret",
|
||||
kv_secret_deleted_text:
|
||||
"This secret version has been soft deleted but remains restorable, do you want to restore it?",
|
||||
kv_secret_restore_btn: "Restore Secret Version",
|
||||
kv_secret_loading: "Loading Secret..",
|
||||
kv_secret_delete_btn: "Delete",
|
||||
kv_secret_delete_all_btn: "Delete All Versions",
|
||||
kv_secret_delete_version_btn: "Delete Version {{ version }}",
|
||||
kv_secret_edit_btn: "Edit",
|
||||
kv_secret_versions_btn: "Versions",
|
||||
|
||||
// Key Value Secret Editor Page
|
||||
"kv_sec_edit_title": "K/V Edit",
|
||||
"kv_sec_edit_btn": "Edit",
|
||||
"kv_sec_edit_loading": "Loading Editor..",
|
||||
"kv_sec_edit_invalid_json_err": "Invalid JSON",
|
||||
"kv_sec_edit_suffix": " (edit)",
|
||||
kv_sec_edit_title: "K/V Edit",
|
||||
kv_sec_edit_btn: "Edit",
|
||||
kv_sec_edit_loading: "Loading Editor..",
|
||||
kv_sec_edit_invalid_json_err: "Invalid JSON",
|
||||
kv_sec_edit_suffix: " (edit)",
|
||||
|
||||
// Key Value Secret Versions Page
|
||||
"kv_sec_versions_title": "K/V Versions",
|
||||
"kv_sec_versions_suffix": " (versions)",
|
||||
kv_sec_versions_title: "K/V Versions",
|
||||
kv_sec_versions_suffix: " (versions)",
|
||||
|
||||
// Key Value View/List Secrets Page
|
||||
"kv_view_title": "K/V View",
|
||||
"kv_view_cubbyhole_text": "In cubbyhole, secrets can be stored as long as the lease of your token is valid. They will be deleted when lease is expired and can only be viewed by your current token.",
|
||||
"kv_view_new_btn": "New",
|
||||
"kv_view_none_here_text": "You seem to have no secrets here, would you like to create one?",
|
||||
kv_view_title: "K/V View",
|
||||
kv_view_cubbyhole_text:
|
||||
"In cubbyhole, secrets can be stored as long as the lease of your token is valid. They will be deleted when lease is expired and can only be viewed by your current token.",
|
||||
kv_view_new_btn: "New",
|
||||
kv_view_none_here_text: "You seem to have no secrets here, would you like to create one?",
|
||||
|
||||
// TOTP View Page
|
||||
"totp_view_title": "TOTP",
|
||||
"totp_view_new_btn": "Add new TOTP key",
|
||||
"totp_view_loading": "Loading TOTP Codes..",
|
||||
"totp_view_empty": "You seem to have no TOTP codes here, would you like to create one?",
|
||||
"totp_view_loading_box": "Loading..",
|
||||
totp_view_title: "TOTP",
|
||||
totp_view_new_btn: "Add new TOTP key",
|
||||
totp_view_loading: "Loading TOTP Codes..",
|
||||
totp_view_empty: "You seem to have no TOTP codes here, would you like to create one?",
|
||||
totp_view_loading_box: "Loading..",
|
||||
|
||||
// New TOTP Key Page
|
||||
"totp_new_title": "New TOTP Key",
|
||||
"totp_new_suffix": " (new)",
|
||||
"totp_new_name_text": "TOTP Key Name",
|
||||
"totp_new_info": "You need either a key or a URI, URI prefered but may not work. Just scan the QR code and copy the URL.",
|
||||
"totp_new_uri_input": "URI",
|
||||
"totp_new_key_input": "Key",
|
||||
"totp_new_add_btn": "Add TOTP Key",
|
||||
totp_new_title: "New TOTP Key",
|
||||
totp_new_suffix: " (new)",
|
||||
totp_new_name_text: "TOTP Key Name",
|
||||
totp_new_info:
|
||||
"You need either a key or a URI, URI prefered but may not work. Just scan the QR code and copy the URL.",
|
||||
totp_new_uri_input: "URI",
|
||||
totp_new_key_input: "Key",
|
||||
totp_new_add_btn: "Add TOTP Key",
|
||||
|
||||
// Transit View Page
|
||||
"transit_view_title": "Transit View",
|
||||
"transit_view_none_here_text": "You seem to have no transit keys here, would you like to create one?",
|
||||
transit_view_title: "Transit View",
|
||||
transit_view_none_here_text:
|
||||
"You seem to have no transit keys here, would you like to create one?",
|
||||
|
||||
// Transit View Secret Page
|
||||
"transit_view_secret_title": "Transit Secret View",
|
||||
"transit_view_encrypt_text": "Encrypt",
|
||||
"transit_view_encrypt_icon_text": "Encryption Icon",
|
||||
"transit_view_encrypt_description": "Encrypt some plaintext or base64 encoded binary.",
|
||||
"transit_view_decrypt_text": "Decrypt",
|
||||
"transit_view_decrypt_description": "Decrypt some cyphertext.",
|
||||
"transit_view_decrypt_icon_text": "Decryption Icon",
|
||||
"transit_view_rewrap_text": "Rewrap",
|
||||
"transit_view_rewrap_description": "Rewrap ciphertext using a different key version.",
|
||||
"transit_view_rewrap_icon_text": "Rewrap Icon",
|
||||
transit_view_secret_title: "Transit Secret View",
|
||||
transit_view_encrypt_text: "Encrypt",
|
||||
transit_view_encrypt_icon_text: "Encryption Icon",
|
||||
transit_view_encrypt_description: "Encrypt some plaintext or base64 encoded binary.",
|
||||
transit_view_decrypt_text: "Decrypt",
|
||||
transit_view_decrypt_description: "Decrypt some cyphertext.",
|
||||
transit_view_decrypt_icon_text: "Decryption Icon",
|
||||
transit_view_rewrap_text: "Rewrap",
|
||||
transit_view_rewrap_description: "Rewrap ciphertext using a different key version.",
|
||||
transit_view_rewrap_icon_text: "Rewrap Icon",
|
||||
|
||||
// Transit Encrypt Page
|
||||
"transit_encrypt_title": "Transit Encrypt",
|
||||
"transit_encrypt_suffix": " (encrypt)",
|
||||
"transit_encrypt_input_placeholder": "Plaintext or base64",
|
||||
"transit_encrypt_already_encoded_checkbox": "Is the data already encoded in base64?",
|
||||
"transit_encrypt_encrypt_btn": "Encrypt",
|
||||
"transit_encrypt_encryption_result_modal_title": "Encryption Result",
|
||||
transit_encrypt_title: "Transit Encrypt",
|
||||
transit_encrypt_suffix: " (encrypt)",
|
||||
transit_encrypt_input_placeholder: "Plaintext or base64",
|
||||
transit_encrypt_already_encoded_checkbox: "Is the data already encoded in base64?",
|
||||
transit_encrypt_encrypt_btn: "Encrypt",
|
||||
transit_encrypt_encryption_result_modal_title: "Encryption Result",
|
||||
|
||||
// Transit Decrypt Page
|
||||
"transit_decrypt_title": "Transit Decrypt",
|
||||
"transit_decrypt_suffix": " (decrypt)",
|
||||
"transit_decrypt_input_placeholder": "Cyphertext",
|
||||
"transit_decrypt_decode_checkbox": "Should the plaintext be base64 decoded?",
|
||||
"transit_decrypt_decrypt_btn": "Decrypt",
|
||||
"transit_decrypt_decryption_result_modal_title": "Decryption Result",
|
||||
transit_decrypt_title: "Transit Decrypt",
|
||||
transit_decrypt_suffix: " (decrypt)",
|
||||
transit_decrypt_input_placeholder: "Cyphertext",
|
||||
transit_decrypt_decode_checkbox: "Should the plaintext be base64 decoded?",
|
||||
transit_decrypt_decrypt_btn: "Decrypt",
|
||||
transit_decrypt_decryption_result_modal_title: "Decryption Result",
|
||||
|
||||
// Transit Rewrap Page
|
||||
"transit_rewrap_title": "Transit Rewrap",
|
||||
"transit_rewrap_suffix": " (rewrap)",
|
||||
"transit_rewrap_version_option_text": "{{version_num}}",
|
||||
"transit_rewrap_latest_version_option_text": "{{version_num}} (latest)",
|
||||
"transit_rewrap_input_placeholder": "Cyphertext",
|
||||
"transit_rewrap_rewrap_btn": "Rewrap",
|
||||
"transit_rewrap_result_modal_title": "Rewrap Result",
|
||||
}
|
||||
transit_rewrap_title: "Transit Rewrap",
|
||||
transit_rewrap_suffix: " (rewrap)",
|
||||
transit_rewrap_version_option_text: "{{version_num}}",
|
||||
transit_rewrap_latest_version_option_text: "{{version_num}} (latest)",
|
||||
transit_rewrap_input_placeholder: "Cyphertext",
|
||||
transit_rewrap_rewrap_btn: "Rewrap",
|
||||
transit_rewrap_result_modal_title: "Rewrap Result",
|
||||
};
|
||||
|
|
205
src/translations/fr.js
vendored
205
src/translations/fr.js
vendored
|
@ -1,147 +1,152 @@
|
|||
module.exports = {
|
||||
// The localised name for the language
|
||||
"language_name": "Français",
|
||||
language_name: "Français",
|
||||
// Internal: The direction of text (ltr or rtl)
|
||||
"language_direction": "ltr",
|
||||
language_direction: "ltr",
|
||||
|
||||
// These are the buttons on the top bar.
|
||||
"home_btn": "Accueil",
|
||||
"back_btn": "Retour",
|
||||
"refresh_btn": "Rafraichir",
|
||||
"me_btn": "Profil/Paramètres",
|
||||
home_btn: "Accueil",
|
||||
back_btn: "Retour",
|
||||
refresh_btn: "Rafraichir",
|
||||
me_btn: "Profil/Paramètres",
|
||||
|
||||
// General Notification Messages
|
||||
"notification_copy_success": "Copié dans le presse-papiers.",
|
||||
notification_copy_success: "Copié dans le presse-papiers.",
|
||||
|
||||
// Copyable Modal
|
||||
"copy_modal_download_btn": "Télécharger",
|
||||
"copy_modal_copy_btn": "Copier",
|
||||
"copy_modal_close_btn": "Fermer",
|
||||
copy_modal_download_btn: "Télécharger",
|
||||
copy_modal_copy_btn: "Copier",
|
||||
copy_modal_close_btn: "Fermer",
|
||||
|
||||
// Copyable Input Box
|
||||
"copy_input_box_copy_icon_text": "Bouton Copier",
|
||||
copy_input_box_copy_icon_text: "Bouton Copier",
|
||||
|
||||
// Me Page
|
||||
"me_page_title": "Profil/Paramètres",
|
||||
"log_out_btn": "Déconnexion",
|
||||
"seal_vault_btn": "Verrouiller le coffre",
|
||||
"copy_token_btn": "Copier le jeton",
|
||||
"renew_lease_btn": "Renouveler le jeton",
|
||||
"change_language_btn": "Changer la langue",
|
||||
me_page_title: "Profil/Paramètres",
|
||||
log_out_btn: "Déconnexion",
|
||||
seal_vault_btn: "Verrouiller le coffre",
|
||||
copy_token_btn: "Copier le jeton",
|
||||
renew_lease_btn: "Renouveler le jeton",
|
||||
change_language_btn: "Changer la langue",
|
||||
|
||||
// Home Page
|
||||
"home_page_title": "Accueil",
|
||||
"vaulturl_text": "Adresse du Vault: {{text}}",
|
||||
"password_generator_btn": "Générateur de mot de passe",
|
||||
"your_token_expires_in": "Votre jeton expire dans {{date, until_date}}",
|
||||
home_page_title: "Accueil",
|
||||
vaulturl_text: "Adresse du Vault: {{text}}",
|
||||
password_generator_btn: "Générateur de mot de passe",
|
||||
your_token_expires_in: "Votre jeton expire dans {{date, until_date}}",
|
||||
|
||||
// Unseal Page
|
||||
"unseal_vault_text": "Ouvrir le Vault",
|
||||
"submit_key_btn": "Ajouter la clé",
|
||||
"unseal_input_btn": "Basculer en entrée de clé manuelle",
|
||||
"unseal_qr_btn": "Basculer en entrée de QR code",
|
||||
"key_input_placeholder": "Clé",
|
||||
"unseal_keys_progress": "Clés: {{progress}}/{{keys_needed}}",
|
||||
unseal_vault_text: "Ouvrir le Vault",
|
||||
submit_key_btn: "Ajouter la clé",
|
||||
unseal_input_btn: "Basculer en entrée de clé manuelle",
|
||||
unseal_qr_btn: "Basculer en entrée de QR code",
|
||||
key_input_placeholder: "Clé",
|
||||
unseal_keys_progress: "Clés: {{progress}}/{{keys_needed}}",
|
||||
|
||||
// Language Selector Page
|
||||
"set_language_title": "Langue",
|
||||
"set_language_btn": "Changer la langue",
|
||||
set_language_title: "Langue",
|
||||
set_language_btn: "Changer la langue",
|
||||
|
||||
// Password Generator Page
|
||||
"password_generator_title": "Générateur de mot de passe",
|
||||
"gen_password_btn": "Générer le mot de passe",
|
||||
password_generator_title: "Générateur de mot de passe",
|
||||
gen_password_btn: "Générer le mot de passe",
|
||||
|
||||
// Login Page
|
||||
"log_in_title": "Connexion",
|
||||
"log_in_with_token": "Jeton",
|
||||
"log_in_with_username": "Nom d'utilisateur",
|
||||
"token_input": "Jeton",
|
||||
"username_input": "Nom d'utilisateur",
|
||||
"password_input": "Mot de passe",
|
||||
"log_in_btn": "Se connecter",
|
||||
log_in_title: "Connexion",
|
||||
log_in_with_token: "Jeton",
|
||||
log_in_with_username: "Nom d'utilisateur",
|
||||
token_input: "Jeton",
|
||||
username_input: "Nom d'utilisateur",
|
||||
password_input: "Mot de passe",
|
||||
log_in_btn: "Se connecter",
|
||||
|
||||
// Key Value Delete Page
|
||||
"kv_delete_title": "Suppression clé/valeur",
|
||||
"kv_delete_text": "Voulez-vous vraiment supprimer ceci ?",
|
||||
"kv_delete_btn": "Supprimer",
|
||||
"kv_delete_suffix": " (supprimé)",
|
||||
kv_delete_title: "Suppression clé/valeur",
|
||||
kv_delete_text: "Voulez-vous vraiment supprimer ceci ?",
|
||||
kv_delete_btn: "Supprimer",
|
||||
kv_delete_suffix: " (supprimé)",
|
||||
|
||||
// Key Value New Page
|
||||
"kv_new_title": "Nouvelle clé/valeur",
|
||||
"kv_new_suffix": " (nouveau)",
|
||||
"kv_new_path": "Chemin relatif",
|
||||
"kv_new_create_btn": "Créer un secret vide",
|
||||
kv_new_title: "Nouvelle clé/valeur",
|
||||
kv_new_suffix: " (nouveau)",
|
||||
kv_new_path: "Chemin relatif",
|
||||
kv_new_create_btn: "Créer un secret vide",
|
||||
|
||||
// Key Value Secret Page
|
||||
"kv_secret_title": "Clé/valeur secrète",
|
||||
"kv_secret_deleted_text": "Cette version secrète a été supprimée temporairement mais reste restaurable, voulez-vous la restaurer ?",
|
||||
"kv_secret_restore_btn": "Restaurer la version secrète",
|
||||
"kv_secret_loading": "Chargement du secret..",
|
||||
"kv_secret_delete_btn": "Supprimer",
|
||||
"kv_secret_delete_all_btn": "Supprimer toutes les versions",
|
||||
"kv_secret_delete_version_btn": "Supprimer la version {{ version }}",
|
||||
"kv_secret_edit_btn": "Éditer",
|
||||
"kv_secret_versions_btn": "Versions",
|
||||
kv_secret_title: "Clé/valeur secrète",
|
||||
kv_secret_deleted_text:
|
||||
"Cette version secrète a été supprimée temporairement mais reste restaurable, voulez-vous la restaurer ?",
|
||||
kv_secret_restore_btn: "Restaurer la version secrète",
|
||||
kv_secret_loading: "Chargement du secret..",
|
||||
kv_secret_delete_btn: "Supprimer",
|
||||
kv_secret_delete_all_btn: "Supprimer toutes les versions",
|
||||
kv_secret_delete_version_btn: "Supprimer la version {{ version }}",
|
||||
kv_secret_edit_btn: "Éditer",
|
||||
kv_secret_versions_btn: "Versions",
|
||||
|
||||
// Key Value Secret Editor Page
|
||||
"kv_sec_edit_title": "Édition clé/valeur",
|
||||
"kv_sec_edit_btn": "Éditer",
|
||||
"kv_sec_edit_loading": "Chargement de l'éditeur..",
|
||||
"kv_sec_edit_invalid_json_err": "Code JSON invalide",
|
||||
"kv_sec_edit_suffix": " (édité)",
|
||||
kv_sec_edit_title: "Édition clé/valeur",
|
||||
kv_sec_edit_btn: "Éditer",
|
||||
kv_sec_edit_loading: "Chargement de l'éditeur..",
|
||||
kv_sec_edit_invalid_json_err: "Code JSON invalide",
|
||||
kv_sec_edit_suffix: " (édité)",
|
||||
|
||||
// Key Value Secret Versions Page
|
||||
"kv_sec_versions_title": "Versions clé/valeur",
|
||||
"kv_sec_versions_suffix": " (versions)",
|
||||
kv_sec_versions_title: "Versions clé/valeur",
|
||||
kv_sec_versions_suffix: " (versions)",
|
||||
|
||||
// Key Value View/List Secrets Page
|
||||
"kv_view_title": "Visionneuse clé/valeur",
|
||||
"kv_view_cubbyhole_text": "Dans cubbyhole, les secrets peuvent être stockés aussi longtemps que la validité de votre jeton. Ils seront supprimés quand le délai est expiré et ne peuvent être lus qu'avec votre jeton actuel.",
|
||||
"kv_view_new_btn": "Nouveau",
|
||||
"kv_view_none_here_text": "Vous semblez ne pas avoir de secrets ici, voulez-vous en créer un ?",
|
||||
kv_view_title: "Visionneuse clé/valeur",
|
||||
kv_view_cubbyhole_text:
|
||||
"Dans cubbyhole, les secrets peuvent être stockés aussi longtemps que la validité de votre jeton. Ils seront supprimés quand le délai est expiré et ne peuvent être lus qu'avec votre jeton actuel.",
|
||||
kv_view_new_btn: "Nouveau",
|
||||
kv_view_none_here_text: "Vous semblez ne pas avoir de secrets ici, voulez-vous en créer un ?",
|
||||
|
||||
// TOTP View Page
|
||||
"totp_view_title": "TOTP",
|
||||
"totp_view_new_btn": "Ajouter une clé TOTP",
|
||||
"totp_view_loading": "Chargement des codes TOTP..",
|
||||
"totp_view_empty": "Vous ne semblez pas avoir de code TOTP ici, voulez-vous en créer un ?",
|
||||
"totp_view_loading_box": "Chargement..",
|
||||
totp_view_title: "TOTP",
|
||||
totp_view_new_btn: "Ajouter une clé TOTP",
|
||||
totp_view_loading: "Chargement des codes TOTP..",
|
||||
totp_view_empty: "Vous ne semblez pas avoir de code TOTP ici, voulez-vous en créer un ?",
|
||||
totp_view_loading_box: "Chargement..",
|
||||
|
||||
// New TOTP Key Page
|
||||
"totp_new_title": "Nouvelle clé TOTP",
|
||||
"totp_new_suffix": " (nouveau)",
|
||||
"totp_new_name_text": "Nom de clé TOTP",
|
||||
"totp_new_info": "Vous avez besoin soit d'une clé ou d'une URI, une URI est recommandée mais peut ne pas fonctionner. Scannez simplement le QR code et copiez l'adresse.",
|
||||
"totp_new_uri_input": "URI",
|
||||
"totp_new_key_input": "Clé",
|
||||
"totp_new_add_btn": "Ajouter clé TOTP",
|
||||
totp_new_title: "Nouvelle clé TOTP",
|
||||
totp_new_suffix: " (nouveau)",
|
||||
totp_new_name_text: "Nom de clé TOTP",
|
||||
totp_new_info:
|
||||
"Vous avez besoin soit d'une clé ou d'une URI, une URI est recommandée mais peut ne pas fonctionner. Scannez simplement le QR code et copiez l'adresse.",
|
||||
totp_new_uri_input: "URI",
|
||||
totp_new_key_input: "Clé",
|
||||
totp_new_add_btn: "Ajouter clé TOTP",
|
||||
|
||||
// Transit View Page
|
||||
"transit_view_title": "Fenêtre Transit",
|
||||
"transit_view_none_here_text": "Vous semblez ne pas avoir de clés de transit ici, voulez-vous en créer une ?",
|
||||
transit_view_title: "Fenêtre Transit",
|
||||
transit_view_none_here_text:
|
||||
"Vous semblez ne pas avoir de clés de transit ici, voulez-vous en créer une ?",
|
||||
|
||||
// Transit View Secret Page
|
||||
"transit_view_secret_title": "Transit Secret",
|
||||
"transit_view_encrypt_text": "Chiffrer",
|
||||
"transit_view_encrypt_icon_text": "Icône chiffrement",
|
||||
"transit_view_encrypt_description": "Chiffrer du texte brut ou données binaires encodées en base64.",
|
||||
"transit_view_decrypt_text": "Déchiffrer",
|
||||
"transit_view_decrypt_description": "Déchiffrer du texte chiffré.",
|
||||
"transit_view_decrypt_icon_text": "Icône déchiffrement",
|
||||
transit_view_secret_title: "Transit Secret",
|
||||
transit_view_encrypt_text: "Chiffrer",
|
||||
transit_view_encrypt_icon_text: "Icône chiffrement",
|
||||
transit_view_encrypt_description:
|
||||
"Chiffrer du texte brut ou données binaires encodées en base64.",
|
||||
transit_view_decrypt_text: "Déchiffrer",
|
||||
transit_view_decrypt_description: "Déchiffrer du texte chiffré.",
|
||||
transit_view_decrypt_icon_text: "Icône déchiffrement",
|
||||
|
||||
// Transit Encrypt Page
|
||||
"transit_encrypt_title": "Chiffrement Transit",
|
||||
"transit_encrypt_suffix": " (chiffrer)",
|
||||
"transit_encrypt_input_placeholder": "Texte brut ou base64",
|
||||
"transit_encrypt_already_encoded_checkbox": "Les données sont-elles déjà encodées en base64 ?",
|
||||
"transit_encrypt_encrypt_btn": "Chiffrer",
|
||||
"transit_encrypt_encryption_result_modal_title": "Résultat chiffré",
|
||||
transit_encrypt_title: "Chiffrement Transit",
|
||||
transit_encrypt_suffix: " (chiffrer)",
|
||||
transit_encrypt_input_placeholder: "Texte brut ou base64",
|
||||
transit_encrypt_already_encoded_checkbox: "Les données sont-elles déjà encodées en base64 ?",
|
||||
transit_encrypt_encrypt_btn: "Chiffrer",
|
||||
transit_encrypt_encryption_result_modal_title: "Résultat chiffré",
|
||||
|
||||
// Transit decrypt Page
|
||||
"transit_decrypt_title": "Déchiffrement Transit",
|
||||
"transit_decrypt_suffix": " (déchiffrer)",
|
||||
"transit_decrypt_input_placeholder": "Message chiffré",
|
||||
"transit_decrypt_decode_checkbox": "Est-ce que le texte brut doit être encodé en base64 ?",
|
||||
"transit_decrypt_decrypt_btn": "Déchiffrer",
|
||||
"transit_decrypt_decryption_result_modal_title": "Résultat déchiffré",
|
||||
}
|
||||
transit_decrypt_title: "Déchiffrement Transit",
|
||||
transit_decrypt_suffix: " (déchiffrer)",
|
||||
transit_decrypt_input_placeholder: "Message chiffré",
|
||||
transit_decrypt_decode_checkbox: "Est-ce que le texte brut doit être encodé en base64 ?",
|
||||
transit_decrypt_decrypt_btn: "Déchiffrer",
|
||||
transit_decrypt_decryption_result_modal_title: "Résultat déchiffré",
|
||||
};
|
||||
|
|
30
src/translations/nl.js
vendored
30
src/translations/nl.js
vendored
|
@ -1,24 +1,24 @@
|
|||
module.exports = {
|
||||
// The localised name for the language
|
||||
"language_name": "Nederlands",
|
||||
language_name: "Nederlands",
|
||||
// Internal: The direction of text (ltr or rtl)
|
||||
"language_direction": "ltr",
|
||||
language_direction: "ltr",
|
||||
|
||||
// These are the buttons on the top bar.
|
||||
"home_btn": "Homepagina",
|
||||
"back_btn": "Terug",
|
||||
"refresh_btn": "Ververs",
|
||||
"me_btn": "Profiel/Instellingen",
|
||||
home_btn: "Homepagina",
|
||||
back_btn: "Terug",
|
||||
refresh_btn: "Ververs",
|
||||
me_btn: "Profiel/Instellingen",
|
||||
// These are the page titles
|
||||
"me_page_title": "Profiel/Instellingen",
|
||||
"home_page_title": "Homepagina",
|
||||
me_page_title: "Profiel/Instellingen",
|
||||
home_page_title: "Homepagina",
|
||||
|
||||
// These are all o the other translations
|
||||
"log_out_btn": "Log Uit",
|
||||
"copy_token_btn": "Kopieer Token",
|
||||
"renew_lease_btn": "Vernieuw Token-lease",
|
||||
log_out_btn: "Log Uit",
|
||||
copy_token_btn: "Kopieer Token",
|
||||
renew_lease_btn: "Vernieuw Token-lease",
|
||||
|
||||
"vaulturl_text": "Vault URL: {{text}}",
|
||||
"password_generator_btn": "Wachtwoordgenerator",
|
||||
"your_token_expires_in": "Uw token vervalt in {{date, until_date}}"
|
||||
}
|
||||
vaulturl_text: "Vault URL: {{text}}",
|
||||
password_generator_btn: "Wachtwoordgenerator",
|
||||
your_token_expires_in: "Uw token vervalt in {{date, until_date}}",
|
||||
};
|
||||
|
|
229
src/translations/ru.js
vendored
229
src/translations/ru.js
vendored
|
@ -1,164 +1,167 @@
|
|||
module.exports = {
|
||||
// The localised name for the language
|
||||
"language_name": "русский",
|
||||
language_name: "русский",
|
||||
// Internal: The direction of text (ltr or rtl)
|
||||
"language_direction": "ltr",
|
||||
language_direction: "ltr",
|
||||
|
||||
// These are the buttons on the top bar.
|
||||
"home_btn": "Главная страница",
|
||||
"back_btn": "Назад",
|
||||
"refresh_btn": "Обновить",
|
||||
"me_btn": "Профиль/параметры",
|
||||
home_btn: "Главная страница",
|
||||
back_btn: "Назад",
|
||||
refresh_btn: "Обновить",
|
||||
me_btn: "Профиль/параметры",
|
||||
|
||||
// General Notification Messages
|
||||
"notification_copy_success": "Текст скопирован в буфер обмена.",
|
||||
notification_copy_success: "Текст скопирован в буфер обмена.",
|
||||
|
||||
// Copyable Modal
|
||||
"copy_modal_download_btn": "Загрузить",
|
||||
"copy_modal_copy_btn": "Копировать",
|
||||
"copy_modal_close_btn": "Закрыть",
|
||||
copy_modal_download_btn: "Загрузить",
|
||||
copy_modal_copy_btn: "Копировать",
|
||||
copy_modal_close_btn: "Закрыть",
|
||||
|
||||
// Copyable Input Box
|
||||
"copy_input_box_copy_icon_text": "Копировать",
|
||||
copy_input_box_copy_icon_text: "Копировать",
|
||||
|
||||
// File Upload Input
|
||||
"file_upload_input_btn": "Загрузить файл",
|
||||
file_upload_input_btn: "Загрузить файл",
|
||||
|
||||
// Me Page
|
||||
"me_page_title": "Профиль/параметры",
|
||||
"log_out_btn": "Выход",
|
||||
"seal_vault_btn": "Закрыть хранилище",
|
||||
"copy_token_btn": "Копировать код доступа",
|
||||
"renew_lease_btn": "Продлить действие кода доступа",
|
||||
"change_language_btn": "Выбор языка",
|
||||
me_page_title: "Профиль/параметры",
|
||||
log_out_btn: "Выход",
|
||||
seal_vault_btn: "Закрыть хранилище",
|
||||
copy_token_btn: "Копировать код доступа",
|
||||
renew_lease_btn: "Продлить действие кода доступа",
|
||||
change_language_btn: "Выбор языка",
|
||||
|
||||
// Home Page
|
||||
"home_page_title": "Главная страница",
|
||||
"vaulturl_text": "Адрес хранилища: {{text}}",
|
||||
"password_generator_btn": "Генератор паролей",
|
||||
"your_token_expires_in": "Продолжительность ключа: {{date, until_date}}",
|
||||
home_page_title: "Главная страница",
|
||||
vaulturl_text: "Адрес хранилища: {{text}}",
|
||||
password_generator_btn: "Генератор паролей",
|
||||
your_token_expires_in: "Продолжительность ключа: {{date, until_date}}",
|
||||
|
||||
// Unseal Page
|
||||
"unseal_vault_text": "Раскрыть хранилище",
|
||||
"submit_key_btn": "Отправить ключ",
|
||||
"unseal_input_btn": "Ввести ключ вручную",
|
||||
"unseal_qr_btn": "Ввести ключ через QR-код",
|
||||
"key_input_placeholder": "Ключ",
|
||||
"unseal_keys_progress": "Ключи: {{progress}}/{{keys_needed}}",
|
||||
unseal_vault_text: "Раскрыть хранилище",
|
||||
submit_key_btn: "Отправить ключ",
|
||||
unseal_input_btn: "Ввести ключ вручную",
|
||||
unseal_qr_btn: "Ввести ключ через QR-код",
|
||||
key_input_placeholder: "Ключ",
|
||||
unseal_keys_progress: "Ключи: {{progress}}/{{keys_needed}}",
|
||||
|
||||
// Language Selector Page
|
||||
"set_language_title": "Выбор языка",
|
||||
"set_language_btn": "Выбрать язык",
|
||||
set_language_title: "Выбор языка",
|
||||
set_language_btn: "Выбрать язык",
|
||||
|
||||
// Password Generator Page
|
||||
"password_generator_title": "Генератор паролей",
|
||||
"password_length_title": "Длина пароля ({{min}}/{{max}})",
|
||||
"gen_password_btn": "Генерировать",
|
||||
password_generator_title: "Генератор паролей",
|
||||
password_length_title: "Длина пароля ({{min}}/{{max}})",
|
||||
gen_password_btn: "Генерировать",
|
||||
|
||||
// Login Page
|
||||
"log_in_title": "Вход",
|
||||
"log_in_with_token": "Вход с кодом доступа",
|
||||
"log_in_with_username": "Вход по имени",
|
||||
"token_input": "Код доступа",
|
||||
"username_input": "Имя пользователя",
|
||||
"password_input": "Пароль",
|
||||
"log_in_btn": "Войти",
|
||||
"token_login_error": "Неправильный код доступа",
|
||||
log_in_title: "Вход",
|
||||
log_in_with_token: "Вход с кодом доступа",
|
||||
log_in_with_username: "Вход по имени",
|
||||
token_input: "Код доступа",
|
||||
username_input: "Имя пользователя",
|
||||
password_input: "Пароль",
|
||||
log_in_btn: "Войти",
|
||||
token_login_error: "Неправильный код доступа",
|
||||
|
||||
// Key Value Delete Page
|
||||
"kv_delete_title": "Удаление ключа/значения",
|
||||
"kv_delete_text": "Вы уверены, что хотите удалить это?",
|
||||
"kv_delete_btn": "Удалить",
|
||||
"kv_delete_suffix": " (удалить)",
|
||||
kv_delete_title: "Удаление ключа/значения",
|
||||
kv_delete_text: "Вы уверены, что хотите удалить это?",
|
||||
kv_delete_btn: "Удалить",
|
||||
kv_delete_suffix: " (удалить)",
|
||||
|
||||
// Key Value New Page
|
||||
"kv_new_title": "Новый ключ/значение",
|
||||
"kv_new_suffix": " (новый)",
|
||||
"kv_new_path": "Относительный путь",
|
||||
"kv_new_create_btn": "Создать пустые тайные данные",
|
||||
kv_new_title: "Новый ключ/значение",
|
||||
kv_new_suffix: " (новый)",
|
||||
kv_new_path: "Относительный путь",
|
||||
kv_new_create_btn: "Создать пустые тайные данные",
|
||||
|
||||
// Key Value Secret Page
|
||||
"kv_secret_title": "Тайные ключи/значения",
|
||||
"kv_secret_deleted_text": "Тайная версия данных была удалена, но может быть восстановлена. Вы хотите восстановить её?",
|
||||
"kv_secret_restore_btn": "Восстановить тайную версию",
|
||||
"kv_secret_loading": "Загрузка тайных данных..",
|
||||
"kv_secret_delete_btn": "Удалить",
|
||||
"kv_secret_delete_all_btn": "Удалить все версии",
|
||||
"kv_secret_delete_version_btn": "Удалить версию {{ version }}",
|
||||
"kv_secret_edit_btn": "Редактировать",
|
||||
"kv_secret_versions_btn": "Версии",
|
||||
kv_secret_title: "Тайные ключи/значения",
|
||||
kv_secret_deleted_text:
|
||||
"Тайная версия данных была удалена, но может быть восстановлена. Вы хотите восстановить её?",
|
||||
kv_secret_restore_btn: "Восстановить тайную версию",
|
||||
kv_secret_loading: "Загрузка тайных данных..",
|
||||
kv_secret_delete_btn: "Удалить",
|
||||
kv_secret_delete_all_btn: "Удалить все версии",
|
||||
kv_secret_delete_version_btn: "Удалить версию {{ version }}",
|
||||
kv_secret_edit_btn: "Редактировать",
|
||||
kv_secret_versions_btn: "Версии",
|
||||
|
||||
// Key Value Secret Editor Page
|
||||
"kv_sec_edit_title": "Редактировать ключи/значения",
|
||||
"kv_sec_edit_btn": "Редактировать",
|
||||
"kv_sec_edit_loading": "Загрузка редактора..",
|
||||
"kv_sec_edit_invalid_json_err": "Некорректный JSON",
|
||||
"kv_sec_edit_suffix": " (редакт.)",
|
||||
kv_sec_edit_title: "Редактировать ключи/значения",
|
||||
kv_sec_edit_btn: "Редактировать",
|
||||
kv_sec_edit_loading: "Загрузка редактора..",
|
||||
kv_sec_edit_invalid_json_err: "Некорректный JSON",
|
||||
kv_sec_edit_suffix: " (редакт.)",
|
||||
|
||||
// Key Value Secret Versions Page
|
||||
"kv_sec_versions_title": "Версии ключей/значений",
|
||||
"kv_sec_versions_suffix": " (версии)",
|
||||
kv_sec_versions_title: "Версии ключей/значений",
|
||||
kv_sec_versions_suffix: " (версии)",
|
||||
|
||||
// Key Value View/List Secrets Page
|
||||
"kv_view_title": "Просмотр ключей/значений",
|
||||
"kv_view_cubbyhole_text": "При использовании cubbyhole, тайные данные можно хранить до тех пор, пока действителен ваш код доступа. Они будут удалены при истечении кода и могут быть просмотрены только с использованием этого кода.",
|
||||
"kv_view_new_btn": "Создать",
|
||||
"kv_view_none_here_text": "У вас на данный момент нет тайных данных. Хотите ли вы их создать?",
|
||||
kv_view_title: "Просмотр ключей/значений",
|
||||
kv_view_cubbyhole_text:
|
||||
"При использовании cubbyhole, тайные данные можно хранить до тех пор, пока действителен ваш код доступа. Они будут удалены при истечении кода и могут быть просмотрены только с использованием этого кода.",
|
||||
kv_view_new_btn: "Создать",
|
||||
kv_view_none_here_text: "У вас на данный момент нет тайных данных. Хотите ли вы их создать?",
|
||||
|
||||
// TOTP View Page
|
||||
"totp_view_title": "TOTP",
|
||||
"totp_view_new_btn": "Добавить ключ TOTP",
|
||||
"totp_view_loading": "Загрузка кодов TOTP..",
|
||||
"totp_view_empty": "У вас на данный момент нет кодов TOTP. Хотите ли вы их создать?",
|
||||
"totp_view_loading_box": "Загрузка..",
|
||||
totp_view_title: "TOTP",
|
||||
totp_view_new_btn: "Добавить ключ TOTP",
|
||||
totp_view_loading: "Загрузка кодов TOTP..",
|
||||
totp_view_empty: "У вас на данный момент нет кодов TOTP. Хотите ли вы их создать?",
|
||||
totp_view_loading_box: "Загрузка..",
|
||||
|
||||
// New TOTP Key Page
|
||||
"totp_new_title": "Новый ключ TOTP",
|
||||
"totp_new_suffix": " (новый)",
|
||||
"totp_new_name_text": "Имя ключа TOTP",
|
||||
"totp_new_info": "Необходим либо ключ, либо адрес URI. Лучше всего использовать URI, но он может не сработать. Отсканируйте QR-код и скопируйте его адрес.",
|
||||
"totp_new_uri_input": "URI",
|
||||
"totp_new_key_input": "Ключ",
|
||||
"totp_new_add_btn": "Добавить ключ TOTP",
|
||||
totp_new_title: "Новый ключ TOTP",
|
||||
totp_new_suffix: " (новый)",
|
||||
totp_new_name_text: "Имя ключа TOTP",
|
||||
totp_new_info:
|
||||
"Необходим либо ключ, либо адрес URI. Лучше всего использовать URI, но он может не сработать. Отсканируйте QR-код и скопируйте его адрес.",
|
||||
totp_new_uri_input: "URI",
|
||||
totp_new_key_input: "Ключ",
|
||||
totp_new_add_btn: "Добавить ключ TOTP",
|
||||
|
||||
// Transit View Page
|
||||
"transit_view_title": "Ключи Transit",
|
||||
"transit_view_none_here_text": "У вас нет ключей Transit, хотите ли вы их создать?",
|
||||
transit_view_title: "Ключи Transit",
|
||||
transit_view_none_here_text: "У вас нет ключей Transit, хотите ли вы их создать?",
|
||||
|
||||
// Transit View Secret Page
|
||||
"transit_view_secret_title": "Просмотр тайных данных Transit",
|
||||
"transit_view_encrypt_text": "Зашифровать",
|
||||
"transit_view_encrypt_icon_text": "Значок шифрования",
|
||||
"transit_view_encrypt_description": "Зашифровать текст или двоичные данные, закодированные base64.",
|
||||
"transit_view_decrypt_text": "Расшифровать",
|
||||
"transit_view_decrypt_description": "Расшифровать шифр-текст.",
|
||||
"transit_view_decrypt_icon_text": "Значок расшифрования",
|
||||
"transit_view_rewrap_text": "Перешифровать",
|
||||
"transit_view_rewrap_description": "Перешифровать шифр-текст, используя другую версию ключа.",
|
||||
"transit_view_rewrap_icon_text": "Значок перешифрования",
|
||||
transit_view_secret_title: "Просмотр тайных данных Transit",
|
||||
transit_view_encrypt_text: "Зашифровать",
|
||||
transit_view_encrypt_icon_text: "Значок шифрования",
|
||||
transit_view_encrypt_description: "Зашифровать текст или двоичные данные, закодированные base64.",
|
||||
transit_view_decrypt_text: "Расшифровать",
|
||||
transit_view_decrypt_description: "Расшифровать шифр-текст.",
|
||||
transit_view_decrypt_icon_text: "Значок расшифрования",
|
||||
transit_view_rewrap_text: "Перешифровать",
|
||||
transit_view_rewrap_description: "Перешифровать шифр-текст, используя другую версию ключа.",
|
||||
transit_view_rewrap_icon_text: "Значок перешифрования",
|
||||
|
||||
// Transit Encrypt Page
|
||||
"transit_encrypt_title": "Шифрование Transit",
|
||||
"transit_encrypt_suffix": " (шифр.)",
|
||||
"transit_encrypt_input_placeholder": "Текст или base64",
|
||||
"transit_encrypt_already_encoded_checkbox": "Данные уже закодированы в base64?",
|
||||
"transit_encrypt_encrypt_btn": "Зашифровать",
|
||||
"transit_encrypt_encryption_result_modal_title": "Результат шифрования",
|
||||
transit_encrypt_title: "Шифрование Transit",
|
||||
transit_encrypt_suffix: " (шифр.)",
|
||||
transit_encrypt_input_placeholder: "Текст или base64",
|
||||
transit_encrypt_already_encoded_checkbox: "Данные уже закодированы в base64?",
|
||||
transit_encrypt_encrypt_btn: "Зашифровать",
|
||||
transit_encrypt_encryption_result_modal_title: "Результат шифрования",
|
||||
|
||||
// Transit Decrypt Page
|
||||
"transit_decrypt_title": "Расшифрование Transit",
|
||||
"transit_decrypt_suffix": " (расшифр.)",
|
||||
"transit_decrypt_input_placeholder": "Шифр-текст",
|
||||
"transit_decrypt_decode_checkbox": "Нужно ли раскодировать текст из base64?",
|
||||
"transit_decrypt_decrypt_btn": "Расшифровать",
|
||||
"transit_decrypt_decryption_result_modal_title": "Результат расшифрования",
|
||||
transit_decrypt_title: "Расшифрование Transit",
|
||||
transit_decrypt_suffix: " (расшифр.)",
|
||||
transit_decrypt_input_placeholder: "Шифр-текст",
|
||||
transit_decrypt_decode_checkbox: "Нужно ли раскодировать текст из base64?",
|
||||
transit_decrypt_decrypt_btn: "Расшифровать",
|
||||
transit_decrypt_decryption_result_modal_title: "Результат расшифрования",
|
||||
|
||||
// Transit Rewrap Page
|
||||
"transit_rewrap_title": "Перешифрование Transit",
|
||||
"transit_rewrap_suffix": " (перешифр.)",
|
||||
"transit_rewrap_version_option_text": "{{version_num}}",
|
||||
"transit_rewrap_latest_version_option_text": "{{version_num}} (последняя версия)",
|
||||
"transit_rewrap_input_placeholder": "Шифр-текст",
|
||||
"transit_rewrap_rewrap_btn": "Перешифровать",
|
||||
"transit_rewrap_result_modal_title": "Результат перешифрования",
|
||||
}
|
||||
transit_rewrap_title: "Перешифрование Transit",
|
||||
transit_rewrap_suffix: " (перешифр.)",
|
||||
transit_rewrap_version_option_text: "{{version_num}}",
|
||||
transit_rewrap_latest_version_option_text: "{{version_num}} (последняя версия)",
|
||||
transit_rewrap_input_placeholder: "Шифр-текст",
|
||||
transit_rewrap_rewrap_btn: "Перешифровать",
|
||||
transit_rewrap_result_modal_title: "Результат перешифрования",
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// These are all errors that the user won't see
|
||||
// but are used internally.
|
||||
|
||||
export const DoesNotExistError: Error = new Error("Does not exist.")
|
||||
export const DoesNotExistError: Error = new Error("Does not exist.");
|
||||
|
|
20
src/utils.ts
20
src/utils.ts
|
@ -2,17 +2,19 @@ export function removeDoubleSlash(str: string): string {
|
|||
return str.replace(/\/\/+/g, "/");
|
||||
}
|
||||
|
||||
export const getObjectKeys =
|
||||
(obj: Record<string, unknown>): string[] => Object.getOwnPropertyNames(obj);
|
||||
export const objectToMap =
|
||||
(obj: Record<string, unknown>): Map<string, unknown> => new Map(Object.entries(obj));
|
||||
export const sortedObjectMap =
|
||||
(obj: Record<string, unknown>): Map<string, unknown> => new Map(Object.entries(obj).sort());
|
||||
export const getObjectKeys = (obj: Record<string, unknown>): string[] =>
|
||||
Object.getOwnPropertyNames(obj);
|
||||
export const objectToMap = (obj: Record<string, unknown>): Map<string, unknown> =>
|
||||
new Map(Object.entries(obj));
|
||||
export const sortedObjectMap = (obj: Record<string, unknown>): Map<string, unknown> =>
|
||||
new Map(Object.entries(obj).sort());
|
||||
|
||||
export function getKeyByObjectPropertyValue(map: Record<string, unknown>, searchValue: unknown): string {
|
||||
export function getKeyByObjectPropertyValue(
|
||||
map: Record<string, unknown>,
|
||||
searchValue: unknown,
|
||||
): string {
|
||||
for (const key of getObjectKeys(map)) {
|
||||
if (map[key] === searchValue)
|
||||
return key;
|
||||
if (map[key] === searchValue) return key;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue