1
0
Fork 0

Run prettier when using eslint to make code consistantly formatted.

This commit is contained in:
Kitteh 2021-05-12 16:01:04 +01:00
parent 2d0c1d7689
commit 9c50ca2432
74 changed files with 1700 additions and 1534 deletions

View file

@ -1,14 +1,12 @@
{ {
"plugins": [ "plugins": ["sort-imports-es6-autofix", "@typescript-eslint", "prettier"],
"sort-imports-es6-autofix",
"@typescript-eslint"
],
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:import/errors", "plugin:import/errors",
"plugin:import/warnings", "plugin:import/warnings",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking" "plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier"
], ],
"parserOptions": { "parserOptions": {
"ecmaVersion": 12, "ecmaVersion": 12,
@ -23,9 +21,7 @@
"BUILD_STRING": "writable" "BUILD_STRING": "writable"
}, },
"rules": { "rules": {
"no-unused-vars": [ "no-unused-vars": ["off"],
"off"
],
"@typescript-eslint/no-unused-vars": [ "@typescript-eslint/no-unused-vars": [
"error", "error",
{ {
@ -38,15 +34,10 @@
"allow": ["arrowFunctions"] "allow": ["arrowFunctions"]
} }
], ],
"@typescript-eslint/ban-ts-comment": [ "@typescript-eslint/ban-ts-comment": ["off"],
"off" "sort-imports-es6-autofix/sort-imports-es6": [2],
], "@typescript-eslint/no-explicit-any": [2],
"sort-imports-es6-autofix/sort-imports-es6": [ "prettier/prettier": 2
2
],
"@typescript-eslint/no-explicit-any": [
2
]
}, },
"env": { "env": {
"browser": true, "browser": true,
@ -57,10 +48,7 @@
"settings": { "settings": {
"import/resolver": { "import/resolver": {
"node": { "node": {
"extensions": [ "extensions": [".js", ".ts"]
".js",
".ts"
]
} }
} }
} }

6
.prettierrc Normal file
View file

@ -0,0 +1,6 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": false,
"printWidth": 100
}

View file

@ -10,7 +10,9 @@
"css-loader": "^5.2.4", "css-loader": "^5.2.4",
"date-fns": "^2.21.3", "date-fns": "^2.21.3",
"eslint": "^7.26.0", "eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-sort-imports-es6-autofix": "^0.6.0", "eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"git-revision-webpack-plugin": "^5.0.0", "git-revision-webpack-plugin": "^5.0.0",
@ -18,6 +20,7 @@
"i18next": "^20.2.2", "i18next": "^20.2.2",
"mini-css-extract-plugin": "^1.6.0", "mini-css-extract-plugin": "^1.6.0",
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
"prettier": "^2.3.0",
"prismjs": "^1.23.0", "prismjs": "^1.23.0",
"qr-scanner": "^1.2.0", "qr-scanner": "^1.2.0",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",

View file

@ -1,8 +1,6 @@
import { Page } from "./types/Page"; import { Page } from "./types/Page";
import { allPages } from "./allPages" import { allPages } from "./allPages";
import { import { getKeyByObjectPropertyValue } from "./utils";
getKeyByObjectPropertyValue,
} from "./utils";
export class PageState { export class PageState {
constructor() { constructor() {
@ -19,43 +17,42 @@ export class PageState {
// the clunkyness of this approach, but for now, this works. // the clunkyness of this approach, but for now, this works.
get apiURL(): string | null { get apiURL(): string | null {
const apiurl = localStorage.getItem('apiURL') || ""; const apiurl = localStorage.getItem("apiURL") || "";
return apiurl.length > 0 ? apiurl : null; return apiurl.length > 0 ? apiurl : null;
} }
set apiURL(value: string) { set apiURL(value: string) {
localStorage.setItem('apiURL', value); localStorage.setItem("apiURL", value);
} }
get token(): string | null { get token(): string | null {
const tok = localStorage.getItem('token') || ""; const tok = localStorage.getItem("token") || "";
return tok.length > 0 ? tok : null; return tok.length > 0 ? tok : null;
} }
set token(value: string) { set token(value: string) {
localStorage.setItem('token', value); localStorage.setItem("token", value);
} }
get pageDirection(): string { get pageDirection(): string {
return localStorage.getItem('pageDirection') || "ltr"; return localStorage.getItem("pageDirection") || "ltr";
} }
set pageDirection(value: string) { set pageDirection(value: string) {
localStorage.setItem('pageDirection', value); localStorage.setItem("pageDirection", value);
} }
get language(): string { get language(): string {
return localStorage.getItem('language') || ""; return localStorage.getItem("language") || "";
} }
set language(value: string) { set language(value: string) {
localStorage.setItem('language', value); localStorage.setItem("language", value);
} }
get currentBaseMount(): string { get currentBaseMount(): string {
return localStorage.getItem('currentBaseMount') || ""; return localStorage.getItem("currentBaseMount") || "";
} }
set currentBaseMount(value: string) { 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 // Since this is a array we can't act directly on it so we need
// functions to do the same modifications. // functions to do the same modifications.
// See the note at the start o // See the note at the start o
@ -71,47 +68,47 @@ export class PageState {
} }
get currentSecretPath(): string[] { get currentSecretPath(): string[] {
return JSON.parse(localStorage.getItem('currentSecretPath') || "[]") as string[]; return JSON.parse(localStorage.getItem("currentSecretPath") || "[]") as string[];
} }
set currentSecretPath(value: string[]) { set currentSecretPath(value: string[]) {
localStorage.setItem('currentSecretPath', JSON.stringify(value)); localStorage.setItem("currentSecretPath", JSON.stringify(value));
} }
get currentSecretVersion(): string | null { get currentSecretVersion(): string | null {
const result = localStorage.getItem('currentSecretVersion') const result = localStorage.getItem("currentSecretVersion");
return result != "null" ? result || null : null; return result != "null" ? result || null : null;
} }
set currentSecretVersion(value: string) { set currentSecretVersion(value: string) {
localStorage.setItem('currentSecretVersion', String(value)); localStorage.setItem("currentSecretVersion", String(value));
} }
get currentSecret(): string { get currentSecret(): string {
return localStorage.getItem('currentSecret') || ""; return localStorage.getItem("currentSecret") || "";
} }
set currentSecret(value: string) { set currentSecret(value: string) {
localStorage.setItem('currentSecret', value); localStorage.setItem("currentSecret", value);
} }
get currentMountType(): string { get currentMountType(): string {
return localStorage.getItem('currentMountType') || ""; return localStorage.getItem("currentMountType") || "";
} }
set currentMountType(value: string) { set currentMountType(value: string) {
localStorage.setItem('currentMountType', value); localStorage.setItem("currentMountType", value);
} }
get currentPageString(): string { get currentPageString(): string {
const key = getKeyByObjectPropertyValue(allPages, this.currentPage); const key = getKeyByObjectPropertyValue(allPages, this.currentPage);
return key; return key;
} }
get currentPage(): Page | string { get currentPage(): Page | string {
const curPage = localStorage.getItem('currentPage') || "HOME"; const curPage = localStorage.getItem("currentPage") || "HOME";
return allPages[curPage]; return allPages[curPage];
} }
set currentPage(value: Page | string) { set currentPage(value: Page | string) {
if (typeof value == 'object') { if (typeof value == "object") {
const key = getKeyByObjectPropertyValue(allPages, value); const key = getKeyByObjectPropertyValue(allPages, value);
localStorage.setItem('currentPage', key); localStorage.setItem("currentPage", key);
} else { } else {
localStorage.setItem('currentPage', value); localStorage.setItem("currentPage", value);
} }
} }
} }

View file

@ -21,8 +21,8 @@ import { TransitViewSecretPage } from "./pages/Transit/TransitViewSecret";
import { UnsealPage } from "./pages/Unseal"; import { UnsealPage } from "./pages/Unseal";
type pagesList = { type pagesList = {
[key: string]: Page [key: string]: Page;
} };
export const allPages: pagesList = { export const allPages: pagesList = {
HOME: new HomePage(), HOME: new HomePage(),

View file

@ -3,7 +3,7 @@ import { pageState } from "../globalPageState";
export function getHeaders(): Record<string, string> { export function getHeaders(): Record<string, string> {
return { return {
"X-Vault-Token": pageState.token, "X-Vault-Token": pageState.token,
} };
} }
export const appendAPIURL = (url: string): string => pageState.apiURL + url; export const appendAPIURL = (url: string): string => pageState.apiURL + url;

View file

@ -2,15 +2,18 @@ import { appendAPIURL } from "../apiUtils";
export async function usernameLogin(username: string, password: string): Promise<string> { export async function usernameLogin(username: string, password: string): Promise<string> {
const request = new Request(appendAPIURL(`/v1/auth/userpass/login/${username}`), { const request = new Request(appendAPIURL(`/v1/auth/userpass/login/${username}`), {
method: 'POST', method: "POST",
headers: { 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 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) { if ("auth" in data) {
return data.auth.client_token; return data.auth.client_token;
} else if ("errors" in data) { } else if ("errors" in data) {

View file

@ -6,14 +6,14 @@ export async function createOrUpdateSecret(
mountType: string, mountType: string,
secretPath: string[], secretPath: string[],
name: string, name: string,
data: Record<string, unknown> data: Record<string, unknown>,
): Promise<void> { ): Promise<void> {
let secretURL = ""; let secretURL = "";
let APIData = {}; let APIData = {};
if (mountType == "kv-v2") { if (mountType == "kv-v2") {
secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`; secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`;
APIData = { "data": data }; APIData = { data: data };
} else { } else {
secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`; secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`;
APIData = data; APIData = data;
@ -23,14 +23,14 @@ export async function createOrUpdateSecret(
const request = new Request(appendAPIURL(secretURL), { const request = new Request(appendAPIURL(secretURL), {
method: "POST", method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...getHeaders(), ...getHeaders(),
}, },
body: JSON.stringify(APIData, null, 0) body: JSON.stringify(APIData, null, 0),
}); });
const response = await fetch(request); const response = await fetch(request);
if (!response.ok) { 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]); throw new Error(json.errors[0]);
} }
} }

View file

@ -6,7 +6,7 @@ export async function deleteSecret(
mountType: string, mountType: string,
secretPath: string[], secretPath: string[],
name: string, name: string,
version: string | null = null version: string | null = null,
): Promise<void> { ): Promise<void> {
let secretURL = ""; let secretURL = "";
@ -19,9 +19,9 @@ export async function deleteSecret(
method: "POST", method: "POST",
headers: { headers: {
...getHeaders(), ...getHeaders(),
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: version != null ? JSON.stringify({ "versions": [version] }) : "{}" body: version != null ? JSON.stringify({ versions: [version] }) : "{}",
}); });
} else { } else {
if (mountType == "kv-v2") { if (mountType == "kv-v2") {
@ -37,7 +37,7 @@ export async function deleteSecret(
} }
const response = await fetch(request); const response = await fetch(request);
if (!response.ok) { 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]); throw new Error(json.errors[0]);
} }
} }

View file

@ -5,13 +5,12 @@ export async function getSecret(
mountType: string, mountType: string,
secretPath: string[], secretPath: string[],
name: string, name: string,
version: string|null = null version: string | null = null,
): Promise<Record<string, unknown>> { ): Promise<Record<string, unknown>> {
let secretURL = ""; let secretURL = "";
if (mountType == "kv-v2") { if (mountType == "kv-v2") {
secretURL = `/v1/${baseMount}/data/${secretPath.join("")}/${name}`; secretURL = `/v1/${baseMount}/data/${secretPath.join("")}/${name}`;
if (version != null) if (version != null) secretURL += `?version=${version}`;
secretURL += `?version=${version}`;
} else { } else {
secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`; secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
} }
@ -20,10 +19,10 @@ export async function getSecret(
}); });
const resp = await fetch(request); const resp = await fetch(request);
const data = await resp.json() as unknown; const data = (await resp.json()) as unknown;
if (mountType == "kv-v2") { 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 { } else {
return (data as {data: Record<string, unknown>}).data; return (data as { data: Record<string, unknown> }).data;
} }
} }

View file

@ -1,19 +1,22 @@
import { appendAPIURL, getHeaders } from "../apiUtils"; import { appendAPIURL, getHeaders } from "../apiUtils";
type SecretMetadataType = { type SecretMetadataType = {
versions: Record<string, unknown> versions: Record<string, unknown>;
} };
export async function getSecretMetadata( export async function getSecretMetadata(
baseMount: string, baseMount: string,
secretPath: string[], secretPath: string[],
name: string name: string,
): Promise<SecretMetadataType> { ): Promise<SecretMetadataType> {
const request = new Request(appendAPIURL(`/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`), { const request = new Request(
headers: getHeaders(), appendAPIURL(`/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`),
}); {
headers: getHeaders(),
},
);
const resp = await fetch(request); const resp = await fetch(request);
const data = await resp.json() as {data: SecretMetadataType}; const data = (await resp.json()) as { data: SecretMetadataType };
return data.data; return data.data;
} }

View file

@ -4,7 +4,7 @@ import { appendAPIURL, getHeaders } from "../apiUtils";
export async function getSecrets( export async function getSecrets(
baseMount: string, baseMount: string,
mountType: string, mountType: string,
secretPath: string[] secretPath: string[],
): Promise<string[]> { ): Promise<string[]> {
let secretURL = ""; let secretURL = "";
if (mountType == "kv-v2") { if (mountType == "kv-v2") {
@ -20,6 +20,6 @@ export async function getSecrets(
if (resp.status == 404) { if (resp.status == 404) {
throw DoesNotExistError; throw DoesNotExistError;
} }
const data = await resp.json() as { data: { keys: string[] } }; const data = (await resp.json()) as { data: { keys: string[] } };
return data.data.keys; return data.data.keys;
} }

View file

@ -6,16 +6,12 @@ export async function undeleteSecret(
baseMount: string, baseMount: string,
secretPath: string[], secretPath: string[],
name: string, name: string,
version: string|null = null version: string | null = null,
): Promise<void> { ): Promise<void> {
let secretURL = `/v1/${baseMount}/undelete/${secretPath.join("/")}/${name}`; let secretURL = `/v1/${baseMount}/undelete/${secretPath.join("/")}/${name}`;
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, ""); secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
if (version == null) { if (version == null) {
const meta = await getSecretMetadata( const meta = await getSecretMetadata(baseMount, secretPath, name);
baseMount,
secretPath,
name
);
const versions = getObjectKeys(meta.versions); const versions = getObjectKeys(meta.versions);
version = String(versions[versions.length - 1]); version = String(versions[versions.length - 1]);
} }
@ -24,13 +20,13 @@ export async function undeleteSecret(
method: "POST", method: "POST",
headers: { headers: {
...getHeaders(), ...getHeaders(),
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ "versions": [version] }) body: JSON.stringify({ versions: [version] }),
}); });
const response = await fetch(request); const response = await fetch(request);
if (!response.ok) { 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]); throw new Error(json.errors[0]);
} }
} }

View file

@ -1,29 +1,28 @@
import { appendAPIURL, getHeaders } from "../apiUtils"; import { appendAPIURL, getHeaders } from "../apiUtils";
import { removeDoubleSlash } from "../../utils"; import { removeDoubleSlash } from "../../utils";
export async function getCapabilitiesPath(path: string): Promise<string[]> {
export async function getCapabilitiesPath(path: string): Promise<string[]> {
const request = new Request(appendAPIURL("/v1/sys/capabilities-self"), { const request = new Request(appendAPIURL("/v1/sys/capabilities-self"), {
method: "POST", method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...getHeaders(), ...getHeaders(),
}, },
body: JSON.stringify( body: JSON.stringify({
{ paths: [removeDoubleSlash(path)],
"paths": [removeDoubleSlash(path)] }),
}
)
}); });
const response = await fetch(request); const response = await fetch(request);
const data = await response.json() as {capabilities: string[]}; const data = (await response.json()) as { capabilities: string[] };
return data.capabilities; return data.capabilities;
} }
export async function getCapabilities( export async function getCapabilities(
baseMount: string, baseMount: string,
secretPath: string[], secretPath: string[],
name: string name: string,
): Promise<string[]> { ): Promise<string[]> {
return await getCapabilitiesPath(removeDoubleSlash(baseMount + secretPath.join("/") + "/" + name)); return await getCapabilitiesPath(
removeDoubleSlash(baseMount + secretPath.join("/") + "/" + name),
);
} }

View file

@ -1,21 +1,21 @@
import { appendAPIURL, getHeaders } from "../apiUtils"; import { appendAPIURL, getHeaders } from "../apiUtils";
export type MountType = { export type MountType = {
type: string type: string;
options: { options: {
version: string version: string;
} };
} };
export type MountsType = { export type MountsType = {
[key: string]: MountType; [key: string]: MountType;
} };
export async function getMounts(): Promise<MountsType> { export async function getMounts(): Promise<MountsType> {
const request = new Request(appendAPIURL("/v1/sys/internal/ui/mounts"), { const request = new Request(appendAPIURL("/v1/sys/internal/ui/mounts"), {
headers: getHeaders(), headers: getHeaders(),
}); });
const resp = await fetch(request); 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; return data.data.secret;
} }

View file

@ -4,11 +4,11 @@ export type SealStatusType = {
progress: number; progress: number;
t: number; t: number;
sealed: boolean; sealed: boolean;
} };
export async function getSealStatus(): Promise<SealStatusType> { export async function getSealStatus(): Promise<SealStatusType> {
const request = new Request(appendAPIURL("/v1/sys/seal-status")); const request = new Request(appendAPIURL("/v1/sys/seal-status"));
const resp = await fetch(request) const resp = await fetch(request);
const data = await resp.json() as SealStatusType; const data = (await resp.json()) as SealStatusType;
return data; return data;
} }

View file

@ -6,7 +6,7 @@ export async function lookupSelf(): Promise<TokenInfo> {
headers: getHeaders(), headers: getHeaders(),
}); });
const resp = await fetch(request); 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) { if ("data" in data) {
return data.data; return data.data;
} else if ("errors" in data) { } else if ("errors" in data) {

View file

@ -2,15 +2,15 @@ import { appendAPIURL, getHeaders } from "../apiUtils";
export async function renewSelf(): Promise<void> { export async function renewSelf(): Promise<void> {
const request = new Request(appendAPIURL("/v1/auth/token/renew-self"), { const request = new Request(appendAPIURL("/v1/auth/token/renew-self"), {
method: 'POST', method: "POST",
headers: { headers: {
...getHeaders(), ...getHeaders(),
'Content-Type': 'application/json' "Content-Type": "application/json",
}, },
body: JSON.stringify({}) body: JSON.stringify({}),
}); });
const resp = await fetch(request) const resp = await fetch(request);
const data = await resp.json() as { errors?: string[] }; const data = (await resp.json()) as { errors?: string[] };
if ("errors" in data) { if ("errors" in data) {
throw new Error(data.errors[0]); throw new Error(data.errors[0]);
} }

View file

@ -1,13 +1,12 @@
import { appendAPIURL, getHeaders } from "../apiUtils"; import { appendAPIURL, getHeaders } from "../apiUtils";
export async function sealVault(): Promise<void> { export async function sealVault(): Promise<void> {
const request = new Request(appendAPIURL("/v1/sys/seal"), { const request = new Request(appendAPIURL("/v1/sys/seal"), {
method: 'PUT', method: "PUT",
headers: getHeaders(), headers: getHeaders(),
}); });
const resp = await fetch(request) const resp = await fetch(request);
const data = await resp.json() as { errors?: string[] }; const data = (await resp.json()) as { errors?: string[] };
if ("errors" in data) { if ("errors" in data) {
throw new Error(data.errors[0]); throw new Error(data.errors[0]);
} }

View file

@ -4,15 +4,15 @@ export async function submitUnsealKey(key: string): Promise<void> {
const request = new Request(appendAPIURL("/v1/sys/unseal"), { const request = new Request(appendAPIURL("/v1/sys/unseal"), {
method: "POST", method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
"key": key key: key,
}) }),
}); });
const resp = await fetch(request) const resp = await fetch(request);
if (!resp.ok) { if (!resp.ok) {
const data = await resp.json() as { errors?: string[] }; const data = (await resp.json()) as { errors?: string[] };
if ("errors" in data) { if ("errors" in data) {
throw new Error(data.errors[0]); throw new Error(data.errors[0]);
} }

View file

@ -1,18 +1,21 @@
import { appendAPIURL, getHeaders } from "../apiUtils"; import { appendAPIURL, getHeaders } from "../apiUtils";
import { removeDoubleSlash } from "../../utils"; import { removeDoubleSlash } from "../../utils";
export async function addNewTOTP(baseMount: string, parms: {name: string}): Promise<void> { export async function addNewTOTP(baseMount: string, parms: { name: string }): Promise<void> {
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)), { const request = new Request(
method: 'POST', appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)),
headers: { {
'Content-Type': 'application/json', method: "POST",
...getHeaders(), headers: {
"Content-Type": "application/json",
...getHeaders(),
},
body: JSON.stringify(parms),
}, },
body: JSON.stringify(parms) );
}); const resp = await fetch(request);
const resp = await fetch(request)
if (!resp.ok) { if (!resp.ok) {
const data = await resp.json() as { errors?: string[] }; const data = (await resp.json()) as { errors?: string[] };
if ("errors" in data) { if ("errors" in data) {
throw new Error(data.errors[0]); throw new Error(data.errors[0]);
} }

View file

@ -1,11 +1,10 @@
import { appendAPIURL, getHeaders } from "../apiUtils"; import { appendAPIURL, getHeaders } from "../apiUtils";
export async function getTOTPCode(baseMount: string, name: string): Promise<string> { export async function getTOTPCode(baseMount: string, name: string): Promise<string> {
const request = const request = new Request(appendAPIURL(`/v1/${baseMount}/code/${name}`), {
new Request(appendAPIURL(`/v1/${baseMount}/code/${name}`), {
headers: getHeaders(), headers: getHeaders(),
}); });
const resp = await fetch(request) const resp = await fetch(request);
const data = await resp.json() as {data: {code: string}}; const data = (await resp.json()) as { data: { code: string } };
return data.data.code; return data.data.code;
} }

View file

@ -10,6 +10,6 @@ export async function getTOTPKeys(baseMount: string): Promise<string[]> {
if (resp.status == 404) { if (resp.status == 404) {
throw DoesNotExistError; throw DoesNotExistError;
} }
const data = await resp.json() as {data: {keys: string[] }}; const data = (await resp.json()) as { data: { keys: string[] } };
return data.data.keys; return data.data.keys;
} }

View file

@ -11,6 +11,6 @@ export async function getTransitKey(baseMount: string, name: string): Promise<Tr
if (resp.status == 404) { if (resp.status == 404) {
throw DoesNotExistError; throw DoesNotExistError;
} }
const data = await resp.json() as { data: TransitKeyType }; const data = (await resp.json()) as { data: TransitKeyType };
return data.data; return data.data;
} }

View file

@ -9,6 +9,6 @@ export async function getTransitKeys(baseMount: string): Promise<string[]> {
if (resp.status == 404) { if (resp.status == 404) {
throw DoesNotExistError; throw DoesNotExistError;
} }
const data = await resp.json() as { data: string[] }; const data = (await resp.json()) as { data: string[] };
return data.data; return data.data;
} }

View file

@ -3,28 +3,31 @@ import { removeDoubleSlash } from "../../utils";
type DecryptionResult = { type DecryptionResult = {
plaintext: string; plaintext: string;
} };
type DecryptionPayload = { type DecryptionPayload = {
ciphertext: string; ciphertext: string;
} };
export async function transitDecrypt( export async function transitDecrypt(
baseMount: string, baseMount: string,
name: string, name: string,
payload: DecryptionPayload payload: DecryptionPayload,
): Promise<DecryptionResult> { ): Promise<DecryptionResult> {
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/decrypt/${name}`)), { const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/decrypt/${name}`)), {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...getHeaders(), ...getHeaders(),
}, },
body: JSON.stringify(payload) body: JSON.stringify(payload),
}); });
const response = await fetch(request); 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) { if (!response.ok) {
throw new Error(data.errors[0]); throw new Error(data.errors[0]);
} else { } else {

View file

@ -3,28 +3,31 @@ import { removeDoubleSlash } from "../../utils";
type EncryptionResult = { type EncryptionResult = {
ciphertext: string; ciphertext: string;
} };
type EncryptionPayload = { type EncryptionPayload = {
plaintext: string; plaintext: string;
} };
export async function transitEncrypt( export async function transitEncrypt(
baseMount: string, baseMount: string,
name: string, name: string,
payload: EncryptionPayload payload: EncryptionPayload,
): Promise<EncryptionResult> { ): Promise<EncryptionResult> {
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/encrypt/${name}`)), { const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/encrypt/${name}`)), {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...getHeaders(), ...getHeaders(),
}, },
body: JSON.stringify(payload) body: JSON.stringify(payload),
}); });
const response = await fetch(request); 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) { if (!response.ok) {
throw new Error(data.errors[0]); throw new Error(data.errors[0]);
} else { } else {

View file

@ -3,29 +3,32 @@ import { removeDoubleSlash } from "../../utils";
type RewrapResult = { type RewrapResult = {
ciphertext: string; ciphertext: string;
} };
type RewrapPayload = { type RewrapPayload = {
ciphertext: string; ciphertext: string;
key_version?: number; key_version?: number;
} };
export async function transitRewrap( export async function transitRewrap(
baseMount: string, baseMount: string,
name: string, name: string,
payload: RewrapPayload payload: RewrapPayload,
): Promise<RewrapResult> { ): Promise<RewrapResult> {
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/rewrap/${name}`)), { const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/rewrap/${name}`)), {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...getHeaders(), ...getHeaders(),
}, },
body: JSON.stringify(payload) body: JSON.stringify(payload),
}); });
const response = await fetch(request); 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) { if (!response.ok) {
throw new Error(data.errors[0]); throw new Error(data.errors[0]);
} else { } else {

View file

@ -16,4 +16,4 @@ export type TokenInfo = {
policies: string[]; policies: string[];
renewable: boolean; renewable: boolean;
ttl: number; ttl: number;
} };

View file

@ -19,7 +19,7 @@ export type TransitKeyBaseType = {
exportable: boolean; exportable: boolean;
allow_plaintext_backup: boolean; allow_plaintext_backup: boolean;
type: keyof typeof TransitKeyTypes; type: keyof typeof TransitKeyTypes;
} };
// Type returned when calling getTransitKey // Type returned when calling getTransitKey
export type TransitKeyType = TransitKeyBaseType & { export type TransitKeyType = TransitKeyBaseType & {
@ -32,4 +32,4 @@ export type TransitKeyType = TransitKeyBaseType & {
supports_decryption: boolean; supports_decryption: boolean;
supports_derivation: boolean; supports_derivation: boolean;
supports_signing: boolean; supports_signing: boolean;
} };

View file

@ -9,7 +9,7 @@ export interface CopyableInputBoxType extends HTMLElement {
} }
export function CopyableInputBox(text: string, copyable = true): CopyableInputBoxType { 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; let inputBoxCopyButton: HTMLElement = null;
if (copyable) { if (copyable) {
inputBoxCopyButton = makeElement({ inputBoxCopyButton = makeElement({
@ -17,26 +17,23 @@ export function CopyableInputBox(text: string, copyable = true): CopyableInputBo
class: "uk-form-icon", class: "uk-form-icon",
attributes: { attributes: {
"uk-icon": "icon: copy", "uk-icon": "icon: copy",
"role": "img", role: "img",
"aria-label": i18next.t("copy_input_box_copy_icon_text") "aria-label": i18next.t("copy_input_box_copy_icon_text"),
}, },
thenRun: (e) => { thenRun: (e) => {
const clipboard = new ClipboardJS(e); const clipboard = new ClipboardJS(e);
addClipboardNotifications(clipboard, 600); addClipboardNotifications(clipboard, 600);
} },
}); });
} }
const inputBoxInput = makeElement({ const inputBoxInput = makeElement({
tag: "input", tag: "input",
class: ["uk-input", "uk-input-copyable"], class: ["uk-input", "uk-input-copyable"],
attributes: { "readonly": "true", "type": "text" }, attributes: { readonly: "true", type: "text" },
}) as HTMLInputElement; }) as HTMLInputElement;
const inputBoxInner = MarginInline([ const inputBoxInner = MarginInline([inputBoxCopyButton, inputBoxInput]);
inputBoxCopyButton,
inputBoxInput
]);
inputBoxDiv.appendChild(inputBoxInner); inputBoxDiv.appendChild(inputBoxInner);
inputBoxDiv.setText = function (text) { inputBoxDiv.setText = function (text) {
@ -47,6 +44,5 @@ export function CopyableInputBox(text: string, copyable = true): CopyableInputBo
}; };
inputBoxDiv.setText(text); inputBoxDiv.setText(text);
return inputBoxDiv; return inputBoxDiv;
} }

View file

@ -1,24 +1,24 @@
import { addClipboardNotifications } from "../pageUtils"; import { addClipboardNotifications } from "../pageUtils";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import FileSaver from 'file-saver'; import FileSaver from "file-saver";
import UIkit from 'uikit'; import UIkit from "uikit";
import i18next from 'i18next'; import i18next from "i18next";
type FileSaverType = { type FileSaverType = {
saveAs: (blob: Blob, name: string) => void; saveAs: (blob: Blob, name: string) => void;
} };
type ModalType = HTMLElement & { type ModalType = HTMLElement & {
show: () => void; show: () => void;
} };
export function CopyableModal(name: string, contentString: string): ModalType { export function CopyableModal(name: string, contentString: string): ModalType {
const modal = makeElement({ const modal = makeElement({
tag: "div", tag: "div",
class: "modal-sections", class: "modal-sections",
attributes: { attributes: {
"uk-modal": "" "uk-modal": "",
}, },
children: makeElement({ children: makeElement({
tag: "div", tag: "div",
@ -29,8 +29,8 @@ export function CopyableModal(name: string, contentString: string): ModalType {
class: "uk-modal-close-default", class: "uk-modal-close-default",
attributes: { attributes: {
"uk-close": "", "uk-close": "",
type: "button" type: "button",
} },
}), }),
makeElement({ makeElement({
tag: "div", tag: "div",
@ -38,8 +38,8 @@ export function CopyableModal(name: string, contentString: string): ModalType {
children: makeElement({ children: makeElement({
tag: "h2", tag: "h2",
class: "uk-modal-title", class: "uk-modal-title",
text: name text: name,
}) }),
}), }),
makeElement({ makeElement({
tag: "div", tag: "div",
@ -47,8 +47,8 @@ export function CopyableModal(name: string, contentString: string): ModalType {
children: makeElement({ children: makeElement({
tag: "pre", tag: "pre",
class: "wrap-pre", class: "wrap-pre",
text: contentString text: contentString,
}) }),
}), }),
makeElement({ makeElement({
tag: "div", tag: "div",
@ -59,41 +59,42 @@ export function CopyableModal(name: string, contentString: string): ModalType {
class: ["uk-button", "uk-button-primary"], class: ["uk-button", "uk-button-primary"],
attributes: { attributes: {
type: "button", type: "button",
"data-clipboard-text": contentString "data-clipboard-text": contentString,
}, },
text: i18next.t("copy_modal_download_btn"), text: i18next.t("copy_modal_download_btn"),
onclick: () => { 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"); (FileSaver as FileSaverType).saveAs(blob, "result.txt");
} },
}), }),
makeElement({ makeElement({
tag: "button", tag: "button",
class: ["uk-button", "uk-button-primary"], class: ["uk-button", "uk-button-primary"],
attributes: { attributes: {
type: "button", type: "button",
"data-clipboard-text": contentString "data-clipboard-text": contentString,
}, },
text: i18next.t("copy_modal_copy_btn"), text: i18next.t("copy_modal_copy_btn"),
thenRun: (e) => { thenRun: (e) => {
const clipboard = new ClipboardJS(e); const clipboard = new ClipboardJS(e);
addClipboardNotifications(clipboard); addClipboardNotifications(clipboard);
} },
}), }),
makeElement({ makeElement({
tag: "button", tag: "button",
class: ["uk-button", "uk-button-secondary", "uk-modal-close"], class: ["uk-button", "uk-button-secondary", "uk-modal-close"],
attributes: { type: "button" }, attributes: { type: "button" },
text: i18next.t("copy_modal_close_btn") text: i18next.t("copy_modal_close_btn"),
}), }),
],
]
}), }),
] ],
}) }),
}) as ModalType; }) as ModalType;
modal.show = () => { modal.show = () => {
UIkit.modal(modal).show(); UIkit.modal(modal).show();
} };
return modal; return modal;
} }

View file

@ -6,8 +6,8 @@ export function FileUploadInput(name: string): Element {
tag: "input", tag: "input",
attributes: { attributes: {
name: name, name: name,
type: "file" type: "file",
} },
}); });
const selectInput = makeElement({ const selectInput = makeElement({
@ -15,8 +15,8 @@ export function FileUploadInput(name: string): Element {
class: ["uk-input", "uk-form-width-medium"], class: ["uk-input", "uk-form-width-medium"],
attributes: { attributes: {
type: "text", type: "text",
placeholder: i18next.t("file_upload_input_btn") placeholder: i18next.t("file_upload_input_btn"),
} },
}); });
const fileIcon = makeElement({ const fileIcon = makeElement({
@ -24,19 +24,15 @@ export function FileUploadInput(name: string): Element {
class: "uk-form-icon", class: "uk-form-icon",
attributes: { attributes: {
"uk-icon": "icon: upload", "uk-icon": "icon: upload",
"role": "img" role: "img",
}, },
}); });
return makeElement({ return makeElement({
tag: "div", tag: "div",
attributes: { attributes: {
"uk-form-custom": "target: true" "uk-form-custom": "target: true",
}, },
children: [ children: [fileIcon, fileInput, selectInput],
fileIcon,
fileInput,
selectInput
]
}); });
} }

View file

@ -3,6 +3,6 @@ import { makeElement } from "../htmlUtils";
export function ListItem(children: Element[] | Element): HTMLElement { export function ListItem(children: Element[] | Element): HTMLElement {
return makeElement({ return makeElement({
tag: "li", tag: "li",
children: children children: children,
}); });
} }

View file

@ -4,6 +4,6 @@ export function Margin(children: Element | Element[]): Element {
return makeElement({ return makeElement({
tag: "div", tag: "div",
class: "uk-margin", class: "uk-margin",
children: children children: children,
}); });
} }

View file

@ -18,23 +18,35 @@ export function NavBar(): HTMLElement {
tag: "ul", tag: "ul",
class: "uk-navbar-nav", class: "uk-navbar-nav",
children: [ children: [
ListItem(makeElement({ ListItem(
tag: "a", makeElement({
text: i18next.t("home_btn"), tag: "a",
onclick: () => { changePage("HOME"); } text: i18next.t("home_btn"),
})), onclick: () => {
ListItem(makeElement({ changePage("HOME");
tag: "a", },
text: i18next.t("back_btn"), }),
onclick: () => { (pageState.currentPage as Page).goBack(); } ),
})), ListItem(
ListItem(makeElement({ makeElement({
tag: "a", tag: "a",
text: i18next.t("refresh_btn"), text: i18next.t("back_btn"),
onclick: () => { changePage(pageState.currentPageString); } onclick: () => {
})), (pageState.currentPage as Page).goBack();
] },
}) }),
),
ListItem(
makeElement({
tag: "a",
text: i18next.t("refresh_btn"),
onclick: () => {
changePage(pageState.currentPageString);
},
}),
),
],
}),
}), }),
makeElement({ makeElement({
tag: "div", tag: "div",
@ -43,19 +55,22 @@ export function NavBar(): HTMLElement {
tag: "ul", tag: "ul",
class: "uk-navbar-nav", class: "uk-navbar-nav",
children: [ children: [
ListItem(makeElement({ ListItem(
tag: "a", makeElement({
text: i18next.t("me_btn"), tag: "a",
onclick: () => { changePage("ME"); } text: i18next.t("me_btn"),
})) onclick: () => {
] changePage("ME");
}) },
}) }),
] ),
}) ],
}),
}),
],
});
} }
export function reloadNavBar(): void { export function reloadNavBar(): void {
document.querySelector("#navBar").replaceWith(NavBar()); document.querySelector("#navBar").replaceWith(NavBar());
} }

View file

@ -7,6 +7,6 @@ export function Option(label: string, value: string): HTMLElement {
attributes: { attributes: {
label: label, label: label,
value: value, value: value,
} },
}) });
} }

View file

@ -1,31 +1,29 @@
import { Margin } from "./Margin"; import { Margin } from "./Margin";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import QrScanner from 'qr-scanner'; import QrScanner from "qr-scanner";
/* eslint-disable import/no-unresolved */ /* eslint-disable import/no-unresolved */
// @ts-ignore // @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])); QrScanner.WORKER_PATH = URL.createObjectURL(new Blob([qrScannerWorkerSource]));
export interface QRScannerType extends HTMLElement { export interface QRScannerType extends HTMLElement {
deinit(): void; deinit(): void;
} }
export async function QRScanner(onScan: (code: string) => void): Promise<QRScannerType> { export async function QRScanner(onScan: (code: string) => void): Promise<QRScannerType> {
const webcamVideo = makeElement({ const webcamVideo = makeElement({
tag: "video" tag: "video",
}) as HTMLVideoElement; }) as HTMLVideoElement;
const QRInput = makeElement({ const QRInput = makeElement({
tag: "div", tag: "div",
children: [ children: [Margin(webcamVideo)],
Margin(webcamVideo),
]
}) as QRScannerType; }) as QRScannerType;
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
video: { video: {
facingMode: 'environment', facingMode: "environment",
}, },
audio: false, audio: false,
}); });
@ -43,7 +41,7 @@ export async function QRScanner(onScan: (code: string) => void): Promise<QRScann
track.stop(); track.stop();
}); });
} catch (_) { } catch (_) {
()=>{}; () => {};
} }
}; };

View file

@ -7,7 +7,7 @@ type TileParams = {
icon: string; icon: string;
iconText: string; iconText: string;
onclick: () => void; onclick: () => void;
} };
export function Tile(params: TileParams): HTMLElement { export function Tile(params: TileParams): HTMLElement {
if (!params.condition) return; if (!params.condition) return;
@ -28,17 +28,17 @@ export function Tile(params: TileParams): HTMLElement {
class: ["uk-icon", "uk-margin-small-left"], class: ["uk-icon", "uk-margin-small-left"],
attributes: { attributes: {
"uk-icon": `icon: ${params.icon}`, "uk-icon": `icon: ${params.icon}`,
"role": "img", role: "img",
"aria-label": params.iconText "aria-label": params.iconText,
} },
}) }),
}), }),
makeElement({ makeElement({
tag: "span", tag: "span",
class: "uk-text-muted", class: "uk-text-muted",
text: params.description text: params.description,
}) }),
] ],
}) }),
}); });
} }

View file

@ -1,15 +1,15 @@
import { de, enGB, fr, it, nl, ru } from 'date-fns/locale' import { de, enGB, fr, it, nl, ru } from "date-fns/locale";
import { formatDistance as formatDistanceReal} from 'date-fns'; import { formatDistance as formatDistanceReal } from "date-fns";
import { pageState } from "./globalPageState"; import { pageState } from "./globalPageState";
function getLocale(): Locale { function getLocale(): Locale {
return { return {
"en": enGB, en: enGB,
"fr": fr, fr: fr,
"nl": nl, nl: nl,
"ru": ru, ru: ru,
"de": de, de: de,
"it": it, it: it,
}[pageState.language]; }[pageState.language];
} }

View file

@ -1,32 +1,32 @@
import { getObjectKeys } from "./utils"; import { getObjectKeys } from "./utils";
type optionsFunctionsObject = { type optionsFunctionsObject = {
[key: string]: (e: Element, arg: unknown) => void [key: string]: (e: Element, arg: unknown) => void;
} };
const optionsFunctions: optionsFunctionsObject = { const optionsFunctions: optionsFunctionsObject = {
"class": (e: Element, arg: string | string[]) => { class: (e: Element, arg: string | string[]) => {
if (!Array.isArray(arg)) { if (!Array.isArray(arg)) {
arg = String(arg).split(" "); arg = String(arg).split(" ");
} }
e.classList.add(...arg); e.classList.add(...arg);
}, },
"id": (e: Element, arg: string) => e.id = arg, id: (e: Element, arg: string) => (e.id = arg),
"html": (e: Element, arg: string) => e.innerHTML = arg, html: (e: Element, arg: string) => (e.innerHTML = arg),
"onclick": (e: Element, arg: () => void) => (e as HTMLButtonElement).onclick = arg, onclick: (e: Element, arg: () => void) => ((e as HTMLButtonElement).onclick = arg),
"attributes": setElementAttributes, attributes: setElementAttributes,
"text": (e: Element, arg: string) => (e as HTMLParagraphElement).innerText = arg, text: (e: Element, arg: string) => ((e as HTMLParagraphElement).innerText = arg),
"children": (e: Element, arg: Element | Element[]) => { children: (e: Element, arg: Element | Element[]) => {
if (Array.isArray(arg)) { if (Array.isArray(arg)) {
arg.forEach(child => { arg.forEach((child) => {
if (child != null) e.appendChild(child); if (child != null) e.appendChild(child);
}); });
} else { } else {
if (arg != null) e.appendChild(arg); 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 { interface ElementInfo {
condition?: boolean; condition?: boolean;
@ -43,7 +43,11 @@ interface ElementInfo {
} }
export function makeElement(elementInfo: ElementInfo): HTMLElement { 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); const element = document.createElement(elementInfo.tag);
for (const key of Object.getOwnPropertyNames(elementInfo)) { 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> =>
const reader = new FileReader(); new Promise((resolve, reject) => {
reader.readAsDataURL(file); const reader = new FileReader();
reader.onload = () => resolve(reader.result as string); reader.readAsDataURL(file);
reader.onerror = error => reject(error); reader.onload = () => resolve(reader.result as string);
}); reader.onerror = (error) => reject(error);
});

View file

@ -1,4 +1,4 @@
'use strict'; "use strict";
// JS & CSS // JS & CSS
@ -17,51 +17,51 @@ import "prismjs/components/prism-json";
Prism.highlightAll(); Prism.highlightAll();
/* eslint-enable */ /* eslint-enable */
import { import { NavBar } from "./elements/NavBar";
changePage, import { changePage, renderPage } from "./pageUtils";
renderPage,
} from "./pageUtils";
import { getSealStatus } from "./api/sys/getSealStatus"; import { getSealStatus } from "./api/sys/getSealStatus";
import { makeElement } from "./htmlUtils"; import { makeElement } from "./htmlUtils";
import { pageState } from "./globalPageState"; import { pageState } from "./globalPageState";
import { playground } from "./playground"; import { playground } from "./playground";
import { NavBar } from "./elements/NavBar";
// Translations // Translations
import { formatDistance } from './formatDistance'; import { formatDistance } from "./formatDistance";
import i18next from 'i18next'; import i18next from "i18next";
// @ts-ignore // @ts-ignore
import translations from './translations/index.mjs'; import translations from "./translations/index.mjs";
declare global { declare global {
interface Window { pageContent: Element; } interface Window {
pageContent: Element;
}
} }
function onLoad(): void { function onLoad(): void {
document.body.innerHTML = ""; document.body.innerHTML = "";
document.body.appendChild(NavBar()); document.body.appendChild(NavBar());
document.body.appendChild(makeElement({ document.body.appendChild(
tag: "div", makeElement({
class: ["uk-container", "uk-container-medium", "uk-align-center"],
children: makeElement({
tag: "div", tag: "div",
class: ["uk-card", "uk-card-body"], class: ["uk-container", "uk-container-medium", "uk-align-center"],
children: [ children: makeElement({
makeElement({ tag: "div",
tag: "h3", class: ["uk-card", "uk-card-body"],
class: "uk-card-title", children: [
id: "pageTitle", makeElement({
text: "" tag: "h3",
}), class: "uk-card-title",
makeElement({ id: "pageTitle",
tag: "div", text: "",
id: "pageContent" }),
}) makeElement({
] tag: "div",
}) id: "pageContent",
})); }),
],
}),
}),
);
window.pageContent = document.querySelector("#pageContent"); window.pageContent = document.querySelector("#pageContent");
@ -73,7 +73,9 @@ function onLoad(): void {
setInterval(() => { setInterval(() => {
if (pageState.currentPageString != "UNSEAL") { if (pageState.currentPageString != "UNSEAL") {
if (pageState.apiURL.length != 0) { return; } if (pageState.apiURL.length != 0) {
return;
}
void getSealStatus().then((sealStatus) => { void getSealStatus().then((sealStatus) => {
if (sealStatus.sealed) { if (sealStatus.sealed) {
changePage("UNSEAL"); changePage("UNSEAL");
@ -84,23 +86,32 @@ function onLoad(): void {
}, 5000); }, 5000);
} }
document.addEventListener('DOMContentLoaded', function () { document.addEventListener(
console.log("Loading..."); "DOMContentLoaded",
// @ts-expect-error function () {
console.log("Build Data:", BUILD_STRING); console.log("Loading...");
void i18next.init({ // @ts-expect-error
lng: pageState.language, console.log("Build Data:", BUILD_STRING);
fallbackLng: 'en', void i18next
debug: true, .init({
// @ts-ignore lng: pageState.language,
resources: Object.fromEntries(Object.entries(translations).map(([k, v]) => [k, { translation: v }])), fallbackLng: "en",
interpolation: { debug: true,
format: function (value: unknown, format, _): string { // @ts-ignore
if (format === 'until_date' && value instanceof Date) return formatDistance(new Date(), new Date(value)); resources: Object.fromEntries(
return value as string; Object.entries(translations).map(([k, v]) => [k, { translation: v }]),
} ),
} interpolation: {
}).then(function (_) { format: function (value: unknown, format, _): string {
onLoad(); if (format === "until_date" && value instanceof Date)
}); return formatDistance(new Date(), new Date(value));
}, false); return value as string;
},
},
})
.then(function (_) {
onLoad();
});
},
false,
);

View file

@ -5,8 +5,8 @@ import { lookupSelf } from "./api/sys/lookupSelf";
import { makeElement } from "./htmlUtils"; import { makeElement } from "./htmlUtils";
import { pageState } from "./globalPageState"; import { pageState } from "./globalPageState";
import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import UIkit from 'uikit'; import UIkit from "uikit";
import i18next from 'i18next'; import i18next from "i18next";
async function prePageChecksReal() { async function prePageChecksReal() {
if (pageState.language.length == 0) { if (pageState.language.length == 0) {
@ -27,7 +27,7 @@ async function prePageChecksReal() {
try { try {
await lookupSelf(); await lookupSelf();
} catch (e) { } catch (e) {
changePage("LOGIN") changePage("LOGIN");
throw e; throw e;
} }
} }
@ -36,26 +36,29 @@ export async function prePageChecks(): Promise<boolean> {
try { try {
await prePageChecksReal(); await prePageChecksReal();
} catch (e) { } catch (e) {
console.log("OHNO", e) console.log("OHNO", e);
return false; return false;
} }
return true; return true;
} }
export function addClipboardNotifications(clipboard: ClipboardJS, timeout = 1000): void { export function addClipboardNotifications(clipboard: ClipboardJS, timeout = 1000): void {
clipboard.on('success', () => { clipboard.on("success", () => {
UIkit.notification(i18next.t("notification_copy_success"), { UIkit.notification(i18next.t("notification_copy_success"), {
status: 'success', status: "success",
timeout: timeout timeout: timeout,
}); });
}); });
clipboard.on('error', function (e: Error) { clipboard.on("error", function (e: Error) {
UIkit.notification(i18next.t("notification_copy_error", { UIkit.notification(
"error": e.message i18next.t("notification_copy_error", {
}), { error: e.message,
status: 'danger', }),
timeout: timeout {
}); status: "danger",
timeout: timeout,
},
);
}); });
} }
@ -69,9 +72,9 @@ export function setErrorText(text: string): void {
} }
UIkit.notification({ UIkit.notification({
message: `Error: ${text}`, message: `Error: ${text}`,
status: 'danger', status: "danger",
pos: 'top-center', pos: "top-center",
timeout: 2000 timeout: 2000,
}); });
} }
@ -88,13 +91,13 @@ export function changePage(page: string, shouldSwitch = true): void {
export function renderPage(): void { export function renderPage(): void {
document.documentElement.dir = pageState.pageDirection; document.documentElement.dir = pageState.pageDirection;
console.log("Rendering Page: ", (pageState.currentPage as Page).name); console.log("Rendering Page: ", (pageState.currentPage as Page).name);
(document.querySelector("#pageContent") ).innerHTML = ""; document.querySelector("#pageContent").innerHTML = "";
setPageTitle((pageState.currentPage as Page).name); setPageTitle((pageState.currentPage as Page).name);
(pageState.currentPage as Page).render(); (pageState.currentPage as Page).render();
} }
export function setPageTitle(title: string | HTMLElement): void { export function setPageTitle(title: string | HTMLElement): void {
const pageTitle = (document.getElementById("pageTitle") ); const pageTitle = document.getElementById("pageTitle");
pageTitle.innerHTML = ""; pageTitle.innerHTML = "";
if (typeof title === "string") { if (typeof title === "string") {
pageTitle.innerText = title.toString(); pageTitle.innerText = title.toString();
@ -106,7 +109,8 @@ export function setPageTitle(title: string | HTMLElement): void {
function currentTitleSecretText() { function currentTitleSecretText() {
let currentSecretText = pageState.currentSecret; let currentSecretText = pageState.currentSecret;
currentSecretText += (pageState.currentPage as Page).titleSuffix; currentSecretText += (pageState.currentPage as Page).titleSuffix;
if (pageState.currentSecretVersion !== null) currentSecretText += ` (v${pageState.currentSecretVersion})`; if (pageState.currentSecretVersion !== null)
currentSecretText += ` (v${pageState.currentSecretVersion})`;
return currentSecretText; return currentSecretText;
} }
@ -122,14 +126,17 @@ export function setTitleElement(pageState: PageState): void {
pageState.currentSecret = ""; pageState.currentSecret = "";
pageState.currentSecretVersion = null; pageState.currentSecretVersion = null;
if (pageState.currentMountType.startsWith("kv") || pageState.currentMountType == "cubbyhole") { if (
pageState.currentMountType.startsWith("kv") ||
pageState.currentMountType == "cubbyhole"
) {
changePage("KEY_VALUE_VIEW"); changePage("KEY_VALUE_VIEW");
} else if (pageState.currentMountType == "totp") { } else if (pageState.currentMountType == "totp") {
changePage("TOTP"); changePage("TOTP");
} else if (pageState.currentMountType == "transit") { } else if (pageState.currentMountType == "transit") {
changePage("TRANSIT_VIEW"); changePage("TRANSIT_VIEW");
} }
} },
}), }),
...pageState.currentSecretPath.map(function (secretPath, index, secretPaths) { ...pageState.currentSecretPath.map(function (secretPath, index, secretPaths) {
return makeElement({ return makeElement({
@ -141,21 +148,21 @@ export function setTitleElement(pageState: PageState): void {
pageState.currentSecretPath = secretPaths.slice(0, index + 1); pageState.currentSecretPath = secretPaths.slice(0, index + 1);
changePage("KEY_VALUE_VIEW"); changePage("KEY_VALUE_VIEW");
} }
} },
}); });
}), }),
makeElement({ makeElement({
tag: "span", tag: "span",
condition: pageState.currentSecret.length != 0, condition: pageState.currentSecret.length != 0,
text: currentTitleSecretText() text: currentTitleSecretText(),
}) }),
] ],
}); });
setPageTitle(titleElement); setPageTitle(titleElement);
} }
export function setPageContent(content: string | HTMLElement): void { export function setPageContent(content: string | HTMLElement): void {
const pageContent = (document.getElementById("pageContent") ); const pageContent = document.getElementById("pageContent");
if (typeof content === "string") { if (typeof content === "string") {
pageContent.innerHTML = content; pageContent.innerHTML = content;
} else { } else {

View file

@ -5,7 +5,7 @@ import { lookupSelf } from "../api/sys/lookupSelf";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import { pageState } from "../globalPageState"; import { pageState } from "../globalPageState";
import { sortedObjectMap } from "../utils"; import { sortedObjectMap } from "../utils";
import i18next from 'i18next'; import i18next from "i18next";
export class HomePage extends Page { export class HomePage extends Page {
constructor() { constructor() {
@ -15,7 +15,7 @@ export class HomePage extends Page {
setPageContent(""); setPageContent("");
if (!(await prePageChecks())) return; if (!(await prePageChecks())) return;
const homePageContent = makeElement({tag: "div"}); const homePageContent = makeElement({ tag: "div" });
setPageContent(homePageContent); setPageContent(homePageContent);
const textList = makeElement({ const textList = makeElement({
tag: "ul", tag: "ul",
@ -25,8 +25,8 @@ export class HomePage extends Page {
tag: "li", tag: "li",
children: makeElement({ children: makeElement({
tag: "span", tag: "span",
html: i18next.t("vaulturl_text", {"text": pageState.apiURL}) html: i18next.t("vaulturl_text", { text: pageState.apiURL }),
}) }),
}), }),
makeElement({ makeElement({
tag: "li", tag: "li",
@ -35,19 +35,23 @@ export class HomePage extends Page {
text: i18next.t("password_generator_btn"), text: i18next.t("password_generator_btn"),
onclick: () => { onclick: () => {
changePage("PW_GEN"); changePage("PW_GEN");
} },
}) }),
}) }),
] ],
}); });
homePageContent.appendChild(textList); homePageContent.appendChild(textList);
try { try {
const selfTokenInfo = await lookupSelf(); const selfTokenInfo = await lookupSelf();
textList.appendChild(makeElement({ textList.appendChild(
tag: "li", makeElement({
text: i18next.t("your_token_expires_in", {"date": new Date(selfTokenInfo.expire_time)}) tag: "li",
})); text: i18next.t("your_token_expires_in", {
date: new Date(selfTokenInfo.expire_time),
}),
}),
);
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
setErrorText(error.message); setErrorText(error.message);
@ -62,7 +66,10 @@ export class HomePage extends Page {
pageState.currentSecret = ""; pageState.currentSecret = "";
pageState.currentSecretVersion = null; 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); homePageContent.appendChild(navList);
const mounts = await getMounts(); const mounts = await getMounts();
@ -70,10 +77,10 @@ export class HomePage extends Page {
const mountsMap = sortedObjectMap(mounts); const mountsMap = sortedObjectMap(mounts);
mountsMap.forEach(function (mount: MountType, baseMount) { mountsMap.forEach(function (mount: MountType, baseMount) {
if (typeof mount != 'object') return; if (typeof mount != "object") return;
if (mount == null) return; if (mount == null) return;
if (!("type" in mount)) 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; const mountType = mount.type == "kv" ? "kv-v" + String(mount.options.version) : mount.type;
@ -85,26 +92,28 @@ export class HomePage extends Page {
} else if (mount.type == "totp") { } else if (mount.type == "totp") {
linkText = `TOTP - ${baseMount}`; linkText = `TOTP - ${baseMount}`;
linkPage = "TOTP"; linkPage = "TOTP";
} else if (mount.type == "transit"){ } else if (mount.type == "transit") {
linkText = `Transit - ${baseMount}`; linkText = `Transit - ${baseMount}`;
linkPage = "TRANSIT_VIEW"; linkPage = "TRANSIT_VIEW";
} else if (mount.type == "cubbyhole"){ } else if (mount.type == "cubbyhole") {
linkText = `Cubbyhole - ${baseMount}`; linkText = `Cubbyhole - ${baseMount}`;
linkPage = "KEY_VALUE_VIEW"; linkPage = "KEY_VALUE_VIEW";
} }
navList.appendChild(makeElement({ navList.appendChild(
tag: "li", makeElement({
children: makeElement({ tag: "li",
tag: "a", children: makeElement({
text: linkText, tag: "a",
onclick: () => { text: linkText,
pageState.currentBaseMount = baseMount; onclick: () => {
pageState.currentMountType = mountType; pageState.currentBaseMount = baseMount;
changePage(linkPage); pageState.currentMountType = mountType;
} changePage(linkPage);
}) },
})); }),
}),
);
}); });
} }
get name(): string { get name(): string {

View file

@ -3,7 +3,7 @@ import { changePage, setPageContent, setTitleElement } from "../../pageUtils";
import { deleteSecret } from "../../api/kv/deleteSecret"; import { deleteSecret } from "../../api/kv/deleteSecret";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
export class KeyValueDeletePage extends Page { export class KeyValueDeletePage extends Page {
constructor() { constructor() {
@ -20,31 +20,33 @@ export class KeyValueDeletePage extends Page {
} }
render(): void { render(): void {
setTitleElement(pageState); setTitleElement(pageState);
setPageContent(makeElement({ setPageContent(
tag: "div", makeElement({
children: [ tag: "div",
makeElement({ children: [
tag: "h5", makeElement({
text: i18next.t("kv_delete_text") tag: "h5",
}), text: i18next.t("kv_delete_text"),
makeElement({ }),
tag: "button", makeElement({
class: ["uk-button", "uk-button-danger"], tag: "button",
text: i18next.t("kv_delete_btn"), class: ["uk-button", "uk-button-danger"],
onclick: () => { text: i18next.t("kv_delete_btn"),
void deleteSecret( onclick: () => {
pageState.currentBaseMount, void deleteSecret(
pageState.currentMountType, pageState.currentBaseMount,
pageState.currentSecretPath, pageState.currentMountType,
pageState.currentSecret, pageState.currentSecretPath,
pageState.currentSecretVersion, pageState.currentSecret,
).then(() => { pageState.currentSecretVersion,
this.goBack(); ).then(() => {
}); this.goBack();
} });
}), },
] }),
})); ],
}),
);
} }
get titleSuffix(): string { get titleSuffix(): string {
return i18next.t("kv_delete_suffix"); return i18next.t("kv_delete_suffix");

View file

@ -3,7 +3,7 @@ import { changePage, setErrorText, setPageContent, setTitleElement } from "../..
import { createOrUpdateSecret } from "../../api/kv/createOrUpdateSecret"; import { createOrUpdateSecret } from "../../api/kv/createOrUpdateSecret";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
export class KeyValueNewPage extends Page { export class KeyValueNewPage extends Page {
constructor() { constructor() {
@ -32,14 +32,14 @@ export class KeyValueNewPage extends Page {
required: "true", required: "true",
type: "text", type: "text",
placeholder: i18next.t("kv_new_path"), placeholder: i18next.t("kv_new_path"),
name: "path" name: "path",
} },
}) }),
}), }),
makeElement({ makeElement({
tag: "p", tag: "p",
id: "errorText", id: "errorText",
class: "uk-text-danger" class: "uk-text-danger",
}), }),
makeElement({ makeElement({
tag: "button", tag: "button",
@ -47,9 +47,9 @@ export class KeyValueNewPage extends Page {
text: i18next.t("kv_new_create_btn"), text: i18next.t("kv_new_create_btn"),
attributes: { attributes: {
type: "submit", type: "submit",
} },
}) }),
] ],
}) as HTMLFormElement; }) as HTMLFormElement;
setPageContent(this.addKVNewForm); setPageContent(this.addKVNewForm);
@ -65,7 +65,7 @@ export class KeyValueNewPage extends Page {
let keyData = {}; let keyData = {};
if (["kv-v1", "cubbyhole"].includes(pageState.currentMountType)) { if (["kv-v1", "cubbyhole"].includes(pageState.currentMountType)) {
keyData = { "key": "value" }; keyData = { key: "value" };
} }
try { try {
@ -74,14 +74,13 @@ export class KeyValueNewPage extends Page {
pageState.currentMountType, pageState.currentMountType,
pageState.currentSecretPath, pageState.currentSecretPath,
path, path,
keyData keyData,
) );
changePage("KEY_VALUE_VIEW"); changePage("KEY_VALUE_VIEW");
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
setErrorText(error.message); setErrorText(error.message);
} }
} }
get titleSuffix(): string { get titleSuffix(): string {

View file

@ -8,8 +8,7 @@ import { pageState } from "../../globalPageState";
import { sortedObjectMap } from "../../utils"; import { sortedObjectMap } from "../../utils";
import { undeleteSecret } from "../../api/kv/undeleteSecret"; import { undeleteSecret } from "../../api/kv/undeleteSecret";
import Prism from "prismjs"; import Prism from "prismjs";
import i18next from 'i18next'; import i18next from "i18next";
export class KeyValueSecretPage extends Page { export class KeyValueSecretPage extends Page {
constructor() { constructor() {
@ -23,72 +22,86 @@ export class KeyValueSecretPage extends Page {
pageState.currentSecret = ""; pageState.currentSecret = "";
changePage("KEY_VALUE_VIEW"); changePage("KEY_VALUE_VIEW");
} }
} }
async render(): Promise<void> { async render(): Promise<void> {
setTitleElement(pageState); setTitleElement(pageState);
setPageContent(makeElement({ setPageContent(
tag: "div", makeElement({
children: [ tag: "div",
makeElement({ children: [
tag: "p", makeElement({
id: "buttonsBlock" tag: "p",
}), id: "buttonsBlock",
makeElement({ }),
tag: "p", makeElement({
text: i18next.t("kv_secret_loading"), tag: "p",
id: "loadingText" text: i18next.t("kv_secret_loading"),
}), id: "loadingText",
makeElement({ }),
tag: "div", makeElement({
id: "kvList" tag: "div",
}), id: "kvList",
] }),
})); ],
}),
);
const buttonsBlock = document.querySelector("#buttonsBlock"); const buttonsBlock = document.querySelector("#buttonsBlock");
const kvList = document.querySelector("#kvList"); const kvList = document.querySelector("#kvList");
let isSecretNestedJson = false; 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")) { if (caps.includes("delete")) {
let deleteButtonText = i18next.t("kv_secret_delete_btn"); let deleteButtonText = i18next.t("kv_secret_delete_btn");
if (pageState.currentMountType == "kv-v2" && pageState.currentSecretVersion == null) { if (pageState.currentMountType == "kv-v2" && pageState.currentSecretVersion == null) {
deleteButtonText = i18next.t("kv_secret_delete_all_btn"); deleteButtonText = i18next.t("kv_secret_delete_all_btn");
} else if (pageState.currentMountType == "kv-v2" && pageState.currentSecretVersion != null) { } else if (pageState.currentMountType == "kv-v2" && pageState.currentSecretVersion != null) {
deleteButtonText = i18next.t( deleteButtonText = i18next.t("kv_secret_delete_version_btn", {
"kv_secret_delete_version_btn", version: pageState.currentSecretVersion,
{ });
"version": pageState.currentSecretVersion
}
);
} }
buttonsBlock.appendChild(makeElement({ buttonsBlock.appendChild(
tag: "button", makeElement({
id: "deleteButton", tag: "button",
class: ["uk-button", "uk-button-danger"], id: "deleteButton",
onclick: () => { changePage("KEY_VALUE_DELETE"); }, class: ["uk-button", "uk-button-danger"],
text: deleteButtonText onclick: () => {
})); changePage("KEY_VALUE_DELETE");
},
text: deleteButtonText,
}),
);
} }
if (caps.includes("update")) { if (caps.includes("update")) {
if (pageState.currentSecretVersion == null) { if (pageState.currentSecretVersion == null) {
buttonsBlock.appendChild(makeElement({ buttonsBlock.appendChild(
tag: "button", makeElement({
id: "editButton", tag: "button",
class: ["uk-button", "uk-margin", "uk-button-primary"], id: "editButton",
onclick: () => { changePage("KEY_VALUE_SECRET_EDIT"); }, class: ["uk-button", "uk-margin", "uk-button-primary"],
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") { if (pageState.currentMountType == "kv-v2") {
buttonsBlock.appendChild(makeElement({ buttonsBlock.appendChild(
tag: "button", makeElement({
id: "versionsButton", tag: "button",
class: ["uk-button", "uk-button-secondary"], id: "versionsButton",
onclick: () => { changePage("KEY_VALUE_VERSIONS"); }, class: ["uk-button", "uk-button-secondary"],
text: i18next.t("kv_secret_versions_btn") onclick: () => {
})); changePage("KEY_VALUE_VERSIONS");
},
text: i18next.t("kv_secret_versions_btn"),
}),
);
} }
void getSecret( void getSecret(
@ -96,53 +109,59 @@ export class KeyValueSecretPage extends Page {
pageState.currentMountType, pageState.currentMountType,
pageState.currentSecretPath, pageState.currentSecretPath,
pageState.currentSecret, pageState.currentSecret,
pageState.currentSecretVersion pageState.currentSecretVersion,
).then(secretInfo => { ).then((secretInfo) => {
if (secretInfo == null && pageState.currentMountType == "kv-v2") { if (secretInfo == null && pageState.currentMountType == "kv-v2") {
document.querySelector("#buttonsBlock").remove(); document.querySelector("#buttonsBlock").remove();
document.getElementById("loadingText").remove(); document.getElementById("loadingText").remove();
kvList.appendChild(makeElement({ kvList.appendChild(
tag: "p", makeElement({
text: i18next.t("kv_secret_deleted_text") tag: "p",
})); text: i18next.t("kv_secret_deleted_text"),
}),
);
kvList.appendChild(makeElement({ kvList.appendChild(
tag: "button", makeElement({
text: i18next.t("kv_secret_restore_btn"), tag: "button",
id: "restoreButton", text: i18next.t("kv_secret_restore_btn"),
class: ["uk-button", "uk-button-primary"], id: "restoreButton",
onclick: () => { class: ["uk-button", "uk-button-primary"],
void undeleteSecret( onclick: () => {
pageState.currentBaseMount, void undeleteSecret(
pageState.currentSecretPath, pageState.currentBaseMount,
pageState.currentSecret, pageState.currentSecretPath,
pageState.currentSecretVersion pageState.currentSecret,
).then(_ => { pageState.currentSecretVersion,
changePage(pageState.currentPageString); ).then((_) => {
}); changePage(pageState.currentPageString);
}, });
})); },
}),
);
return; return;
} }
const secretsMap = sortedObjectMap(secretInfo); const secretsMap = sortedObjectMap(secretInfo);
for (const value of secretsMap.values()) { for (const value of secretsMap.values()) {
if (typeof value == 'object') isSecretNestedJson = true; if (typeof value == "object") isSecretNestedJson = true;
} }
if (isSecretNestedJson) { if (isSecretNestedJson) {
const jsonText = JSON.stringify( const jsonText = JSON.stringify(
sortedObjectMap(secretsMap as unknown as Record<string, unknown>), sortedObjectMap(secretsMap as unknown as Record<string, unknown>),
null, null,
4 4,
);
kvList.appendChild(
makeElement({
tag: "pre",
class: ["code-block", "language-json", "line-numbers"],
html: Prism.highlight(jsonText, Prism.languages.json, "json"),
}),
); );
kvList.appendChild(makeElement({
tag: "pre",
class: ["code-block", "language-json", "line-numbers"],
html: Prism.highlight(jsonText, Prism.languages.json, 'json')
}));
} else { } else {
secretsMap.forEach((value: string, key: string) => { secretsMap.forEach((value: string, key: string) => {
const kvListElement = this.makeKVListElement(key, value); const kvListElement = this.makeKVListElement(key, value);
@ -156,7 +175,7 @@ export class KeyValueSecretPage extends Page {
return makeElement({ return makeElement({
tag: "div", tag: "div",
class: ["uk-grid", "uk-grid-small", "uk-text-left"], class: ["uk-grid", "uk-grid-small", "uk-text-left"],
children: [CopyableInputBox(key), CopyableInputBox(value)] children: [CopyableInputBox(key), CopyableInputBox(value)],
}); });
} }

View file

@ -6,7 +6,7 @@ import { getSecret } from "../../api/kv/getSecret";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import { sortedObjectMap, verifyJSONString } from "../../utils"; import { sortedObjectMap, verifyJSONString } from "../../utils";
import i18next from 'i18next'; import i18next from "i18next";
export class KeyValueSecretEditPage extends Page { export class KeyValueSecretEditPage extends Page {
constructor() { constructor() {
@ -19,41 +19,43 @@ export class KeyValueSecretEditPage extends Page {
setTitleElement(pageState); setTitleElement(pageState);
const loadingText = makeElement({ const loadingText = makeElement({
tag: "p", tag: "p",
text: i18next.t("kv_sec_edit_loading") text: i18next.t("kv_sec_edit_loading"),
}); });
const editor = makeElement({ const editor = makeElement({
tag: "div", tag: "div",
class: ["editor", "language-json"] class: ["editor", "language-json"],
}); });
const saveButton = makeElement({ const saveButton = makeElement({
tag: "button", tag: "button",
class: ["uk-button", "uk-button-primary"], class: ["uk-button", "uk-button-primary"],
text: i18next.t("kv_sec_edit_btn") text: i18next.t("kv_sec_edit_btn"),
}); });
setPageContent(makeElement({ setPageContent(
tag: "div", makeElement({
children: [ tag: "div",
loadingText, children: [
editor, loadingText,
makeElement({ editor,
tag: "p", makeElement({
id: "errorText", tag: "p",
class: ["uk-text-danger", "uk-margin-top"] id: "errorText",
}), class: ["uk-text-danger", "uk-margin-top"],
saveButton }),
] saveButton,
})); ],
}),
);
void getSecret( void getSecret(
pageState.currentBaseMount, pageState.currentBaseMount,
pageState.currentMountType, pageState.currentMountType,
pageState.currentSecretPath, pageState.currentSecretPath,
pageState.currentSecret, pageState.currentSecret,
).then(secretInfo => { ).then((secretInfo) => {
loadingText.remove(); loadingText.remove();
const secretsJSON = JSON.stringify(sortedObjectMap(secretInfo), null, 4); 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); jar.updateCode(secretsJSON);
saveButton.onclick = function () { saveButton.onclick = function () {
if (!verifyJSONString(jar.toString())) { if (!verifyJSONString(jar.toString())) {
@ -66,13 +68,15 @@ export class KeyValueSecretEditPage extends Page {
pageState.currentMountType, pageState.currentMountType,
pageState.currentSecretPath, pageState.currentSecretPath,
pageState.currentSecret, pageState.currentSecret,
JSON.parse(jar.toString()) JSON.parse(jar.toString()),
).then(_ => { )
changePage("KEY_VALUE_SECRET"); .then((_) => {
return; changePage("KEY_VALUE_SECRET");
}).catch((e: Error) => { return;
setErrorText(e.message); })
}); .catch((e: Error) => {
setErrorText(e.message);
});
}; };
}); });
} }

View file

@ -4,8 +4,7 @@ import { getSecretMetadata } from "../../api/kv/getSecretMetadata";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { objectToMap } from "../../utils"; import { objectToMap } from "../../utils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
export class KeyValueVersionsPage extends Page { export class KeyValueVersionsPage extends Page {
constructor() { constructor() {
@ -23,28 +22,30 @@ export class KeyValueVersionsPage extends Page {
const versionsList = makeElement({ const versionsList = makeElement({
tag: "ul", tag: "ul",
id: "versionsList", id: "versionsList",
class: ["uk-nav", "uk-nav-default"] class: ["uk-nav", "uk-nav-default"],
}); });
setPageContent(versionsList); setPageContent(versionsList);
const metadata = await getSecretMetadata( const metadata = await getSecretMetadata(
pageState.currentBaseMount, pageState.currentBaseMount,
pageState.currentSecretPath, pageState.currentSecretPath,
pageState.currentSecret pageState.currentSecret,
); );
objectToMap(metadata.versions).forEach((_, ver) => { objectToMap(metadata.versions).forEach((_, ver) => {
versionsList.appendChild(makeElement({ versionsList.appendChild(
tag: "li", makeElement({
children: makeElement({ tag: "li",
tag: "a", children: makeElement({
text: `v${ver}`, tag: "a",
onclick: () => { text: `v${ver}`,
pageState.currentSecretVersion = ver; onclick: () => {
changePage("KEY_VALUE_SECRET"); pageState.currentSecretVersion = ver;
} changePage("KEY_VALUE_SECRET");
}) },
})); }),
}),
);
}); });
} }

View file

@ -4,8 +4,7 @@ import { changePage, setErrorText, setPageContent, setTitleElement } from "../..
import { getSecrets } from "../../api/kv/getSecrets"; import { getSecrets } from "../../api/kv/getSecrets";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
export class KeyValueViewPage extends Page { export class KeyValueViewPage extends Page {
constructor() { constructor() {
@ -28,10 +27,12 @@ export class KeyValueViewPage extends Page {
setPageContent(kvViewPageContent); setPageContent(kvViewPageContent);
if (pageState.currentMountType == "cubbyhole") { if (pageState.currentMountType == "cubbyhole") {
kvViewPageContent.appendChild(makeElement({ kvViewPageContent.appendChild(
tag: "p", makeElement({
text: i18next.t("kv_view_cubbyhole_text"), tag: "p",
})); text: i18next.t("kv_view_cubbyhole_text"),
}),
);
} }
const newButton = makeElement({ const newButton = makeElement({
@ -40,7 +41,7 @@ export class KeyValueViewPage extends Page {
class: ["uk-button", "uk-button-primary", "uk-margin-bottom"], class: ["uk-button", "uk-button-primary", "uk-margin-bottom"],
onclick: () => { onclick: () => {
changePage("KEY_VALUE_NEW_SECRET"); changePage("KEY_VALUE_NEW_SECRET");
} },
}); });
kvViewPageContent.appendChild(newButton); kvViewPageContent.appendChild(newButton);
@ -51,30 +52,32 @@ export class KeyValueViewPage extends Page {
pageState.currentSecretPath, pageState.currentSecretPath,
); );
kvViewPageContent.appendChild(makeElement({ kvViewPageContent.appendChild(
tag: "ul", makeElement({
class: ["uk-nav", "uk-nav-default"], tag: "ul",
children: [ class: ["uk-nav", "uk-nav-default"],
...res.map(function (secret) { children: [
return makeElement({ ...res.map(function (secret) {
tag: "li", return makeElement({
children: makeElement({ tag: "li",
tag: "a", children: makeElement({
text: secret, tag: "a",
onclick: () => { text: secret,
if (secret.endsWith("/")) { onclick: () => {
pageState.pushCurrentSecretPath(secret); if (secret.endsWith("/")) {
changePage("KEY_VALUE_VIEW"); pageState.pushCurrentSecretPath(secret);
} else { changePage("KEY_VALUE_VIEW");
pageState.currentSecret = secret; } else {
changePage("KEY_VALUE_SECRET"); pageState.currentSecret = secret;
} changePage("KEY_VALUE_SECRET");
} }
}) },
}); }),
}) });
] }),
})); ],
}),
);
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
if (error == DoesNotExistError) { if (error == DoesNotExistError) {
@ -82,10 +85,12 @@ export class KeyValueViewPage extends Page {
if (pageState.currentSecretPath.length != 0) { if (pageState.currentSecretPath.length != 0) {
return this.goBack(); return this.goBack();
} else { } else {
kvViewPageContent.appendChild(makeElement({ kvViewPageContent.appendChild(
tag: "p", makeElement({
text: i18next.t("kv_view_none_here_text") tag: "p",
})); text: i18next.t("kv_view_none_here_text"),
}),
);
} }
} else { } else {
setErrorText(error.message); setErrorText(error.message);

View file

@ -6,7 +6,7 @@ import { lookupSelf } from "../api/sys/lookupSelf";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import { pageState } from "../globalPageState"; import { pageState } from "../globalPageState";
import { usernameLogin } from "../api/auth/usernameLogin"; import { usernameLogin } from "../api/auth/usernameLogin";
import i18next from 'i18next'; import i18next from "i18next";
export class LoginPage extends Page { export class LoginPage extends Page {
constructor() { constructor() {
@ -16,140 +16,153 @@ export class LoginPage extends Page {
const tokenLoginForm = makeElement({ const tokenLoginForm = makeElement({
tag: "form", tag: "form",
children: [ children: [
Margin(makeElement({ Margin(
tag: "input", makeElement({
class: ["uk-input", "uk-form-width-medium"], tag: "input",
attributes: { class: ["uk-input", "uk-form-width-medium"],
required: "true", attributes: {
type: "password", required: "true",
placeholder: i18next.t("token_input"), type: "password",
name: "token" placeholder: i18next.t("token_input"),
} name: "token",
})), },
MarginInline(makeElement({ }),
tag: "button", ),
class: ["uk-button", "uk-button-primary"], MarginInline(
text: i18next.t("log_in_btn"), makeElement({
attributes: { tag: "button",
type: "submit" class: ["uk-button", "uk-button-primary"],
} text: i18next.t("log_in_btn"),
})) attributes: {
] type: "submit",
},
}),
),
],
}) as HTMLFormElement; }) as HTMLFormElement;
const usernameLoginForm = makeElement({ const usernameLoginForm = makeElement({
tag: "form", tag: "form",
children: [ children: [
Margin(makeElement({ Margin(
tag: "input", makeElement({
id: "usernameInput", tag: "input",
class: ["uk-input", "uk-form-width-medium"], id: "usernameInput",
attributes: { class: ["uk-input", "uk-form-width-medium"],
required: "true", attributes: {
type: "text", required: "true",
placeholder: i18next.t("username_input"), type: "text",
name: "username" placeholder: i18next.t("username_input"),
} name: "username",
})), },
Margin(makeElement({ }),
tag: "input", ),
id: "passwordInput", Margin(
class: ["uk-input", "uk-form-width-medium"], makeElement({
attributes: { tag: "input",
required: "true", id: "passwordInput",
type: "password", class: ["uk-input", "uk-form-width-medium"],
placeholder: i18next.t("password_input"), attributes: {
name: "password" required: "true",
} type: "password",
})), placeholder: i18next.t("password_input"),
MarginInline(makeElement({ name: "password",
tag: "button", },
class: ["uk-button", "uk-button-primary"], }),
text: i18next.t("log_in_btn"), ),
attributes: { MarginInline(
type: "submit" makeElement({
} tag: "button",
})) class: ["uk-button", "uk-button-primary"],
] text: i18next.t("log_in_btn"),
attributes: {
type: "submit",
},
}),
),
],
}) as HTMLFormElement; }) as HTMLFormElement;
setPageContent(makeElement({ setPageContent(
tag: "div", makeElement({
children: [ tag: "div",
makeElement({ children: [
tag: "ul", makeElement({
class: ["uk-subnav", "uk-subnav-pill"], tag: "ul",
attributes: { "uk-switcher": "" }, class: ["uk-subnav", "uk-subnav-pill"],
children: [ attributes: { "uk-switcher": "" },
makeElement({ children: [
tag: "li", makeElement({
id: "tokenInput", tag: "li",
children: makeElement({ id: "tokenInput",
tag: "a", children: makeElement({
text: i18next.t("log_in_with_token") tag: "a",
}) text: i18next.t("log_in_with_token"),
}), }),
makeElement({ }),
tag: "li", makeElement({
children: makeElement({ tag: "li",
tag: "a", children: makeElement({
text: i18next.t("log_in_with_username") tag: "a",
}) text: i18next.t("log_in_with_username"),
}) }),
] }),
}), ],
makeElement({ }),
tag: "p", makeElement({
id: "errorText", tag: "p",
class: "uk-text-danger" id: "errorText",
}), class: "uk-text-danger",
makeElement({ }),
tag: "ul", makeElement({
class: ["uk-switcher", "uk-margin"], tag: "ul",
children: [ class: ["uk-switcher", "uk-margin"],
makeElement({ children: [
tag: "li", makeElement({
children: tokenLoginForm tag: "li",
}), children: tokenLoginForm,
makeElement({ }),
tag: "li", makeElement({
children: usernameLoginForm tag: "li",
}) children: usernameLoginForm,
] }),
}) ],
] }),
})); ],
}),
);
tokenLoginForm.addEventListener("submit", function (e) { tokenLoginForm.addEventListener("submit", function (e) {
e.preventDefault(); e.preventDefault();
const formData = new FormData(tokenLoginForm); const formData = new FormData(tokenLoginForm);
const token = formData.get("token"); const token = formData.get("token");
pageState.token = token as string; pageState.token = token as string;
lookupSelf().then(_ => { lookupSelf()
changePage("HOME"); .then((_) => {
}).catch((e: Error) => { changePage("HOME");
document.getElementById("tokenInput").classList.add("uk-form-danger"); })
if (e.message == "permission denied") { .catch((e: Error) => {
setErrorText(i18next.t("token_login_error")); document.getElementById("tokenInput").classList.add("uk-form-danger");
} else { if (e.message == "permission denied") {
setErrorText(e.message); setErrorText(i18next.t("token_login_error"));
} } else {
}); setErrorText(e.message);
}
});
}); });
usernameLoginForm.addEventListener("submit", function (e) { usernameLoginForm.addEventListener("submit", function (e) {
e.preventDefault(); e.preventDefault();
const formData = new FormData(usernameLoginForm); const formData = new FormData(usernameLoginForm);
usernameLogin( usernameLogin(formData.get("username") as string, formData.get("password") as string)
formData.get("username") as string, .then((res) => {
formData.get("password") as string, pageState.token = res;
).then(res => { changePage("HOME");
pageState.token = res; })
changePage("HOME"); .catch((e: Error) => {
}).catch((e: Error) => { document.getElementById("usernameInput").classList.add("uk-form-danger");
document.getElementById("usernameInput").classList.add("uk-form-danger"); document.getElementById("passwordInput").classList.add("uk-form-danger");
document.getElementById("passwordInput").classList.add("uk-form-danger"); setErrorText(e.message);
setErrorText(e.message); });
});
}); });
} }

View file

@ -1,13 +1,18 @@
import { Page } from "../types/Page"; 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 { getCapabilitiesPath } from "../api/sys/getCapabilities";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import { pageState } from "../globalPageState"; import { pageState } from "../globalPageState";
import { renewSelf } from "../api/sys/renewSelf"; import { renewSelf } from "../api/sys/renewSelf";
import { sealVault } from "../api/sys/sealVault"; import { sealVault } from "../api/sys/sealVault";
import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import i18next from 'i18next'; import i18next from "i18next";
export class MePage extends Page { export class MePage extends Page {
constructor() { constructor() {
@ -16,80 +21,84 @@ export class MePage extends Page {
async render(): Promise<void> { async render(): Promise<void> {
if (!(await prePageChecks())) return; if (!(await prePageChecks())) return;
setPageContent(makeElement({ setPageContent(
tag: "ul", makeElement({
class: "uk-nav", tag: "ul",
children: [ class: "uk-nav",
makeElement({ children: [
tag: "li", makeElement({
children: makeElement({ tag: "li",
tag: "a", children: makeElement({
text: i18next.t("log_out_btn"), tag: "a",
onclick: () => { text: i18next.t("log_out_btn"),
pageState.token = ""; onclick: () => {
changePage("HOME"); pageState.token = "";
}
})
}),
makeElement({
tag: "li",
children: makeElement({
tag: "a",
text: i18next.t("copy_token_btn"),
attributes: {
"data-clipboard-text": pageState.token,
},
thenRun: (e) => {
const clipboard = new ClipboardJS(e);
addClipboardNotifications(clipboard);
}
})
}),
makeElement({
tag: "li",
children: makeElement({
tag: "a",
text: i18next.t("renew_lease_btn"),
onclick: () => {
renewSelf().then(() => {
changePage("HOME"); changePage("HOME");
}).catch((e: Error) => { },
setErrorText(e.message); }),
}); }),
} makeElement({
}) tag: "li",
}), children: makeElement({
makeElement({ tag: "a",
tag: "li", text: i18next.t("copy_token_btn"),
children: makeElement({ attributes: {
tag: "a", "data-clipboard-text": pageState.token,
condition: await (async () => { },
try { thenRun: (e) => {
const caps = await getCapabilitiesPath("sys/seal"); const clipboard = new ClipboardJS(e);
return caps.includes("sudo") && caps.includes("update"); addClipboardNotifications(clipboard);
} catch (e) { },
return !true; }),
} }),
})(), makeElement({
text: i18next.t("seal_vault_btn"), tag: "li",
onclick: async () => { children: makeElement({
await sealVault(); tag: "a",
changePage("UNSEAL_VAULT"); text: i18next.t("renew_lease_btn"),
} onclick: () => {
}) renewSelf()
}), .then(() => {
makeElement({ changePage("HOME");
tag: "li", })
children: makeElement({ .catch((e: Error) => {
tag: "a", setErrorText(e.message);
text: i18next.t("change_language_btn"), });
onclick: () => { },
changePage("SET_LANGUAGE"); }),
} }),
}) makeElement({
}), tag: "li",
] children: makeElement({
})); tag: "a",
condition: await (async () => {
try {
const caps = await getCapabilitiesPath("sys/seal");
return caps.includes("sudo") && caps.includes("update");
} catch (e) {
return !true;
}
})(),
text: i18next.t("seal_vault_btn"),
onclick: async () => {
await sealVault();
changePage("UNSEAL_VAULT");
},
}),
}),
makeElement({
tag: "li",
children: makeElement({
tag: "a",
text: i18next.t("change_language_btn"),
onclick: () => {
changePage("SET_LANGUAGE");
},
}),
}),
],
}),
);
} }
get name(): string { get name(): string {

View file

@ -4,40 +4,43 @@ import { Option } from "../elements/Option";
import { Page } from "../types/Page"; import { Page } from "../types/Page";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import { setPageContent } from "../pageUtils"; import { setPageContent } from "../pageUtils";
import i18next from 'i18next'; import i18next from "i18next";
const passwordLengthMin = 1; const passwordLengthMin = 1;
const passwordLengthMax = 64; const passwordLengthMax = 64;
const passwordLengthDefault = 24; const passwordLengthDefault = 24;
function random() { 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 window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967295;
} }
return Math.random(); return Math.random();
} }
const lowerCase = 'abcdefghijklmnopqrstuvwxyz'; const lowerCase = "abcdefghijklmnopqrstuvwxyz";
const upperCase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const numbers = '1234567890'; const numbers = "1234567890";
const special = '!#$%&()*+,-./:;<=>?@[]^_{|}~'; const special = "!#$%&()*+,-./:;<=>?@[]^_{|}~";
const alphabets = { const alphabets = {
SECURE: lowerCase + upperCase + numbers + special, SECURE: lowerCase + upperCase + numbers + special,
SMOL: lowerCase + numbers, SMOL: lowerCase + numbers,
HEX: '123456789ABCDEF', HEX: "123456789ABCDEF",
} };
const passwordOptionsDefault = { const passwordOptionsDefault = {
length: passwordLengthDefault, length: passwordLengthDefault,
alphabet: alphabets.SECURE, alphabet: alphabets.SECURE,
} };
function genPassword(options = passwordOptionsDefault) { function genPassword(options = passwordOptionsDefault) {
let pw = ""; let pw = "";
options = { ...passwordOptionsDefault, ...options } options = { ...passwordOptionsDefault, ...options };
const pwArray = options.alphabet.split(''); const pwArray = options.alphabet.split("");
for (let i = 0; i < options.length; i++) { for (let i = 0; i < options.length; i++) {
pw = pw.concat(pwArray[Math.floor(random() * pwArray.length)]); pw = pw.concat(pwArray[Math.floor(random() * pwArray.length)]);
} }
@ -57,11 +60,11 @@ export class PwGenPage extends Page {
render(): void { render(): void {
setPageContent(""); setPageContent("");
this.passwordBox = CopyableInputBox(genPassword(passwordOptionsDefault)) ; this.passwordBox = CopyableInputBox(genPassword(passwordOptionsDefault));
this.passwordLengthTitle = makeElement({ this.passwordLengthTitle = makeElement({
tag: "h4", tag: "h4",
text: this.getPasswordLengthText() text: this.getPasswordLengthText(),
}) as HTMLTitleElement; }) as HTMLTitleElement;
this.passwordLengthRange = makeElement({ this.passwordLengthRange = makeElement({
@ -76,7 +79,7 @@ export class PwGenPage extends Page {
}, },
}) as HTMLInputElement; }) as HTMLInputElement;
this.passwordLengthRange.addEventListener('input', this.updatePassword.bind(this)); this.passwordLengthRange.addEventListener("input", this.updatePassword.bind(this));
this.passwordAlphabet = makeElement({ this.passwordAlphabet = makeElement({
tag: "select", tag: "select",
@ -85,7 +88,7 @@ export class PwGenPage extends Page {
Option("a-z a-Z 0-9 specials", alphabets.SECURE), Option("a-z a-Z 0-9 specials", alphabets.SECURE),
Option("a-z 0-9", alphabets.SMOL), Option("a-z 0-9", alphabets.SMOL),
Option("A-F 1-9", alphabets.HEX), Option("A-F 1-9", alphabets.HEX),
] ],
}) as HTMLSelectElement; }) as HTMLSelectElement;
this.passwordForm = makeElement({ this.passwordForm = makeElement({
@ -95,13 +98,15 @@ export class PwGenPage extends Page {
Margin(this.passwordLengthRange), Margin(this.passwordLengthRange),
Margin(this.passwordAlphabet), Margin(this.passwordAlphabet),
Margin(this.passwordBox), Margin(this.passwordBox),
Margin(makeElement({ Margin(
tag: "button", makeElement({
text: i18next.t("gen_password_btn"), tag: "button",
class: ["uk-button", "uk-button-primary", "uk-margin-bottom"], text: i18next.t("gen_password_btn"),
attributes: { type: "submit" }, class: ["uk-button", "uk-button-primary", "uk-margin-bottom"],
})) attributes: { type: "submit" },
] }),
),
],
}) as HTMLFormElement; }) as HTMLFormElement;
this.passwordForm.addEventListener("submit", (e) => this.formEvent(e)); this.passwordForm.addEventListener("submit", (e) => this.formEvent(e));
@ -111,7 +116,7 @@ export class PwGenPage extends Page {
getPasswordLengthText(): string { getPasswordLengthText(): string {
return i18next.t("password_length_title", { return i18next.t("password_length_title", {
min: this?.passwordLengthRange?.value || 24, min: this?.passwordLengthRange?.value || 24,
max: passwordLengthMax max: passwordLengthMax,
}); });
} }
@ -122,10 +127,12 @@ export class PwGenPage extends Page {
updatePassword(): void { updatePassword(): void {
this.passwordLengthTitle.innerText = this.getPasswordLengthText(); this.passwordLengthTitle.innerText = this.getPasswordLengthText();
this.passwordBox.setText(genPassword({ this.passwordBox.setText(
length: (this.passwordLengthRange.value as unknown) as number, genPassword({
alphabet: this.passwordAlphabet.value , length: this.passwordLengthRange.value as unknown as number,
})); alphabet: this.passwordAlphabet.value,
}),
);
} }
cleanup(): void { cleanup(): void {

View file

@ -3,8 +3,8 @@ import { Page } from "../types/Page";
import { changePage, setPageContent } from "../pageUtils"; import { changePage, setPageContent } from "../pageUtils";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import { pageState } from "../globalPageState"; import { pageState } from "../globalPageState";
import i18next from 'i18next';
import { reloadNavBar } from "../elements/NavBar"; import { reloadNavBar } from "../elements/NavBar";
import i18next from "i18next";
// @ts-ignore // @ts-ignore
import translations from "../translations/index.mjs"; import translations from "../translations/index.mjs";
@ -20,25 +20,26 @@ export class SetLanguagePage extends Page {
tag: "form", tag: "form",
id: "setLanguageForm", id: "setLanguageForm",
children: [ children: [
Margin(makeElement({ Margin(
tag: "select", makeElement({
class: ["uk-select", "uk-form-width-large"], tag: "select",
attributes: { class: ["uk-select", "uk-form-width-large"],
name: "language" attributes: {
}, name: "language",
children: languageIDs.map(function (languageID) { },
return makeElement({ children: languageIDs.map(function (languageID) {
tag: "option", return makeElement({
text: i18next.getFixedT(languageID, null)("language_name"), tag: "option",
attributes: { value: languageID } text: i18next.getFixedT(languageID, null)("language_name"),
}) attributes: { value: languageID },
}) });
}) }),
}),
), ),
makeElement({ makeElement({
tag: "p", tag: "p",
id: "errorText", id: "errorText",
class: "uk-text-danger" class: "uk-text-danger",
}), }),
makeElement({ makeElement({
tag: "button", tag: "button",
@ -46,9 +47,9 @@ export class SetLanguagePage extends Page {
text: i18next.t("set_language_btn"), text: i18next.t("set_language_btn"),
attributes: { attributes: {
type: "submit", type: "submit",
} },
}) }),
] ],
}) as HTMLFormElement; }) as HTMLFormElement;
setPageContent(setLanguageForm); setPageContent(setLanguageForm);
setLanguageForm.addEventListener("submit", function (e) { setLanguageForm.addEventListener("submit", function (e) {

View file

@ -8,42 +8,44 @@ export class SetVaultURLPage extends Page {
super(); super();
} }
render(): void { render(): void {
setPageContent(makeElement({ setPageContent(
tag: "form", makeElement({
id: "setVaultURLForm", tag: "form",
children: [ id: "setVaultURLForm",
makeElement({ children: [
tag: "div", makeElement({
class: "uk-margin", tag: "div",
children: makeElement({ class: "uk-margin",
tag: "input", children: makeElement({
class: ["uk-input", "uk-form-width-medium"], tag: "input",
class: ["uk-input", "uk-form-width-medium"],
attributes: {
required: "true",
type: "text",
placeholder: "Vault URL",
name: "vaultURL",
},
}),
}),
makeElement({
tag: "p",
id: "errorText",
class: "uk-text-danger",
}),
makeElement({
tag: "button",
class: ["uk-button", "uk-button-primary"],
text: "Set",
attributes: { attributes: {
required: "true", type: "submit",
type: "text", },
placeholder: "Vault URL", }),
name: "vaultURL" ],
} }),
}) );
}),
makeElement({
tag: "p",
id: "errorText",
class: "uk-text-danger"
}),
makeElement({
tag: "button",
class: ["uk-button", "uk-button-primary"],
text: "Set",
attributes: {
type: "submit",
}
})
]
}));
document.getElementById("setVaultURLForm").addEventListener("submit", function (e) { document.getElementById("setVaultURLForm").addEventListener("submit", function (e) {
e.preventDefault(); e.preventDefault();
const formData = new FormData(document.querySelector('#setVaultURLForm')); const formData = new FormData(document.querySelector("#setVaultURLForm"));
pageState.apiURL = formData.get("vaultURL") as string; pageState.apiURL = formData.get("vaultURL") as string;
changePage("HOME"); changePage("HOME");
}); });

View file

@ -5,10 +5,10 @@ import { addNewTOTP } from "../../api/totp/addNewTOTP";
import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils"; import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
function replaceAll(str: string, replace: string, replaceWith: string): string { function replaceAll(str: string, replace: string, replaceWith: string): string {
return str.replace(new RegExp(replace, 'g'), replaceWith); return str.replace(new RegExp(replace, "g"), replaceWith);
} }
function removeDashSpaces(str: string): string { function removeDashSpaces(str: string): string {
str = replaceAll(str, "-", ""); str = replaceAll(str, "-", "");
@ -29,52 +29,60 @@ export class NewTOTPPage extends Page {
const totpForm = makeElement({ const totpForm = makeElement({
tag: "form", tag: "form",
children: [ children: [
Margin(makeElement({ Margin(
tag: "input", makeElement({
class: ["uk-input", "uk-form-width-medium"], tag: "input",
attributes: { class: ["uk-input", "uk-form-width-medium"],
required: "true", attributes: {
type: "text", required: "true",
placeholder: i18next.t("totp_new_name_text"), type: "text",
name: "name" placeholder: i18next.t("totp_new_name_text"),
} name: "name",
})), },
}),
),
makeElement({ makeElement({
tag: "p", tag: "p",
text: i18next.t("totp_new_info") text: i18next.t("totp_new_info"),
}), }),
Margin(makeElement({ Margin(
tag: "input", makeElement({
class: ["uk-input", "uk-form-width-medium"], tag: "input",
attributes: { class: ["uk-input", "uk-form-width-medium"],
type: "text", attributes: {
placeholder: i18next.t("totp_new_uri_input"), type: "text",
name: "uri" placeholder: i18next.t("totp_new_uri_input"),
} name: "uri",
})), },
Margin(makeElement({ }),
tag: "input", ),
class: ["uk-input", "uk-form-width-medium"], Margin(
attributes: { makeElement({
type: "text", tag: "input",
placeholder: i18next.t("totp_new_key_input"), class: ["uk-input", "uk-form-width-medium"],
name: "key" attributes: {
} type: "text",
})), placeholder: i18next.t("totp_new_key_input"),
name: "key",
},
}),
),
makeElement({ makeElement({
tag: "p", tag: "p",
id: "errorText", id: "errorText",
class: "uk-text-danger" class: "uk-text-danger",
}), }),
MarginInline(makeElement({ MarginInline(
tag: "button", makeElement({
class: ["uk-button", "uk-button-primary"], tag: "button",
text: i18next.t("totp_new_add_btn"), class: ["uk-button", "uk-button-primary"],
attributes: { text: i18next.t("totp_new_add_btn"),
type: "submit" attributes: {
} type: "submit",
})) },
] }),
),
],
}) as HTMLFormElement; }) as HTMLFormElement;
setPageContent(totpForm); setPageContent(totpForm);
@ -85,13 +93,15 @@ export class NewTOTPPage extends Page {
url: formData.get("uri") as string, url: formData.get("uri") as string,
key: removeDashSpaces(formData.get("key") as string).toUpperCase(), key: removeDashSpaces(formData.get("key") as string).toUpperCase(),
name: formData.get("name") as string, name: formData.get("name") as string,
generate: false generate: false,
}; };
addNewTOTP(pageState.currentBaseMount, parms).then(_ => { addNewTOTP(pageState.currentBaseMount, parms)
changePage("TOTP"); .then((_) => {
}).catch((e: Error) => { changePage("TOTP");
setErrorText(`API Error: ${e.message}`); })
}); .catch((e: Error) => {
setErrorText(`API Error: ${e.message}`);
});
}); });
} }

View file

@ -7,13 +7,12 @@ import { getTOTPKeys } from "../../api/totp/getTOTPKeys";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { objectToMap } from "../../utils"; import { objectToMap } from "../../utils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
export interface TOTPListElement extends HTMLElement { export interface TOTPListElement extends HTMLElement {
setCode(code: string): void; setCode(code: string): void;
} }
export class TOTPViewPage extends Page { export class TOTPViewPage extends Page {
constructor() { constructor() {
super(); super();
@ -27,25 +26,28 @@ export class TOTPViewPage extends Page {
async render(): Promise<void> { async render(): Promise<void> {
setTitleElement(pageState); setTitleElement(pageState);
const totpList = makeElement({ tag: "div" }); const totpList = makeElement({ tag: "div" });
setPageContent(makeElement({ setPageContent(
tag: "div", makeElement({
children: [ tag: "div",
makeElement({ children: [
tag: "a", makeElement({
text: i18next.t("totp_view_new_btn"), tag: "a",
onclick: () => { changePage("NEW_TOTP"); } text: i18next.t("totp_view_new_btn"),
}), onclick: () => {
makeElement({ changePage("NEW_TOTP");
tag: "p", },
id: "loadingText", }),
text: i18next.t("totp_view_loading"), makeElement({
}), tag: "p",
makeElement({ tag: "br" }), id: "loadingText",
makeElement({ tag: "br" }), text: i18next.t("totp_view_loading"),
totpList }),
] makeElement({ tag: "br" }),
})); makeElement({ tag: "br" }),
totpList,
],
}),
);
try { try {
const res = await getTOTPKeys(pageState.currentBaseMount); const res = await getTOTPKeys(pageState.currentBaseMount);
@ -66,14 +68,17 @@ export class TOTPViewPage extends Page {
} }
} }
const totpRefresher = async () => { const totpRefresher = async () => {
await Promise.all(Array.from(objectToMap(this.totpListElements)).map((kv: [string, TOTPListElement]) => { await Promise.all(
return this.updateTOTPElement(...kv); Array.from(objectToMap(this.totpListElements)).map((kv: [string, TOTPListElement]) => {
})) return this.updateTOTPElement(...kv);
} }),
);
};
await totpRefresher(); await totpRefresher();
this.refresher = setInterval(() => { void totpRefresher(); }, 3000) as unknown as number; this.refresher = setInterval(() => {
void totpRefresher();
}, 3000) as unknown as number;
} }
cleanup(): void { cleanup(): void {
@ -92,7 +97,7 @@ export class TOTPViewPage extends Page {
const gridElement = makeElement({ const gridElement = makeElement({
tag: "div", tag: "div",
class: ["uk-grid", "uk-grid-small", "uk-text-expand"], class: ["uk-grid", "uk-grid-small", "uk-text-expand"],
children: [totpKeyBox, totpValueBox] children: [totpKeyBox, totpValueBox],
}) as TOTPListElement; }) as TOTPListElement;
gridElement.setCode = (code: string) => totpValueBox.setText(code); gridElement.setCode = (code: string) => totpValueBox.setText(code);

View file

@ -21,20 +21,24 @@ export class TransitDecryptPage extends Page {
render(): void { render(): void {
setTitleElement(pageState); setTitleElement(pageState);
setPageContent(makeElement({ setPageContent(
tag: "div" makeElement({
})); tag: "div",
}),
);
this.transitDecryptForm = makeElement({ this.transitDecryptForm = makeElement({
tag: "form", tag: "form",
children: [ children: [
Margin(makeElement({ Margin(
tag: "textarea", makeElement({
class: ["uk-textarea", "uk-form-width-medium"], tag: "textarea",
attributes: { class: ["uk-textarea", "uk-form-width-medium"],
placeholder: i18next.t("transit_decrypt_input_placeholder"), attributes: {
name: "ciphertext", placeholder: i18next.t("transit_decrypt_input_placeholder"),
} name: "ciphertext",
})), },
}),
),
Margin(FileUploadInput("ciphertext_file")), Margin(FileUploadInput("ciphertext_file")),
Margin([ Margin([
makeElement({ makeElement({
@ -51,14 +55,14 @@ export class TransitDecryptPage extends Page {
attributes: { attributes: {
type: "checkbox", type: "checkbox",
name: "decodeBase64Checkbox", name: "decodeBase64Checkbox",
} },
}), }),
}), }),
]), ]),
makeElement({ makeElement({
tag: "p", tag: "p",
id: "errorText", id: "errorText",
class: "uk-text-danger" class: "uk-text-danger",
}), }),
makeElement({ makeElement({
tag: "button", tag: "button",
@ -66,9 +70,9 @@ export class TransitDecryptPage extends Page {
text: i18next.t("transit_decrypt_decrypt_btn"), text: i18next.t("transit_decrypt_decrypt_btn"),
attributes: { attributes: {
type: "submit", type: "submit",
} },
}) }),
] ],
}) as HTMLFormElement; }) as HTMLFormElement;
setPageContent(this.transitDecryptForm); setPageContent(this.transitDecryptForm);
this.transitDecryptForm.addEventListener("submit", (e: Event) => { this.transitDecryptForm.addEventListener("submit", (e: Event) => {
@ -86,20 +90,23 @@ export class TransitDecryptPage extends Page {
const ciphertext_file = formData.get("ciphertext_file") as File; const ciphertext_file = formData.get("ciphertext_file") as File;
if (ciphertext_file.size > 0) { 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 { try {
const res = await transitDecrypt( const res = await transitDecrypt(pageState.currentBaseMount, pageState.currentSecret, {
pageState.currentBaseMount, ciphertext: ciphertext,
pageState.currentSecret, });
{ ciphertext: ciphertext },
);
let plaintext = res.plaintext; let plaintext = res.plaintext;
if (decodeBase64 == "on") { if (decodeBase64 == "on") {
plaintext = atob(plaintext); 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); document.body.querySelector("#pageContent").appendChild(modal);
modal.show(); modal.show();
} catch (e: unknown) { } catch (e: unknown) {

View file

@ -8,7 +8,6 @@ import { pageState } from "../../globalPageState";
import { transitEncrypt } from "../../api/transit/transitEncrypt"; import { transitEncrypt } from "../../api/transit/transitEncrypt";
import i18next from "i18next"; import i18next from "i18next";
export class TransitEncryptPage extends Page { export class TransitEncryptPage extends Page {
constructor() { constructor() {
super(); super();
@ -20,23 +19,26 @@ export class TransitEncryptPage extends Page {
transitEncryptForm: HTMLFormElement; transitEncryptForm: HTMLFormElement;
render(): void { render(): void {
setTitleElement(pageState); setTitleElement(pageState);
setPageContent(makeElement({ setPageContent(
tag: "div" makeElement({
})); tag: "div",
}),
);
this.transitEncryptForm = makeElement({ this.transitEncryptForm = makeElement({
tag: "form", tag: "form",
children: [ children: [
Margin(makeElement({ Margin(
tag: "textarea", makeElement({
class: ["uk-textarea", "uk-form-width-medium"], tag: "textarea",
attributes: { class: ["uk-textarea", "uk-form-width-medium"],
placeholder: i18next.t("transit_encrypt_input_placeholder"), attributes: {
name: "plaintext", placeholder: i18next.t("transit_encrypt_input_placeholder"),
} name: "plaintext",
})), },
}),
),
Margin(FileUploadInput("plaintext_file")), Margin(FileUploadInput("plaintext_file")),
Margin([ Margin([
makeElement({ makeElement({
@ -53,14 +55,14 @@ export class TransitEncryptPage extends Page {
attributes: { attributes: {
type: "checkbox", type: "checkbox",
name: "base64Checkbox", name: "base64Checkbox",
} },
}), }),
}), }),
]), ]),
makeElement({ makeElement({
tag: "p", tag: "p",
id: "errorText", id: "errorText",
class: "uk-text-danger" class: "uk-text-danger",
}), }),
makeElement({ makeElement({
tag: "button", tag: "button",
@ -68,9 +70,9 @@ export class TransitEncryptPage extends Page {
text: i18next.t("transit_encrypt_encrypt_btn"), text: i18next.t("transit_encrypt_encrypt_btn"),
attributes: { attributes: {
type: "submit", type: "submit",
} },
}) }),
] ],
}) as HTMLFormElement; }) as HTMLFormElement;
setPageContent(this.transitEncryptForm); setPageContent(this.transitEncryptForm);
@ -89,19 +91,20 @@ export class TransitEncryptPage extends Page {
const plaintext_file = formData.get("plaintext_file") as File; const plaintext_file = formData.get("plaintext_file") as File;
if (plaintext_file.size > 0) { 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; plaintext = base64Checkbox == "on" ? atob(plaintext) : plaintext;
} else { } else {
plaintext = base64Checkbox == "on" ? plaintext : btoa(plaintext); plaintext = base64Checkbox == "on" ? plaintext : btoa(plaintext);
} }
try { try {
const res = await transitEncrypt( const res = await transitEncrypt(pageState.currentBaseMount, pageState.currentSecret, {
pageState.currentBaseMount, plaintext: plaintext,
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); document.body.querySelector("#pageContent").appendChild(modal);
modal.show(); modal.show();
} catch (e: unknown) { } catch (e: unknown) {

View file

@ -10,7 +10,7 @@ import { pageState } from "../../globalPageState";
import { transitRewrap } from "../../api/transit/transitRewrap"; import { transitRewrap } from "../../api/transit/transitRewrap";
import i18next from "i18next"; import i18next from "i18next";
type versionOption = { version: string; label: string } type versionOption = { version: string; label: string };
export class TransitRewrapPage extends Page { export class TransitRewrapPage extends Page {
constructor() { constructor() {
@ -27,7 +27,9 @@ export class TransitRewrapPage extends Page {
setTitleElement(pageState); setTitleElement(pageState);
const transitKey = await getTransitKey(pageState.currentBaseMount, pageState.currentSecret); 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)); const versions = stringVersions.map((val): number => parseInt(val, 10));
// get the selectable version options in the same // get the selectable version options in the same
@ -35,15 +37,15 @@ export class TransitRewrapPage extends Page {
// e.g: ["2 (latest)", "1"] // e.g: ["2 (latest)", "1"]
const options: versionOption[] = versions.map((val): versionOption => { const options: versionOption[] = versions.map((val): versionOption => {
const i18nkey = val == Math.max(...versions) ? const i18nkey =
"transit_rewrap_latest_version_option_text" val == Math.max(...versions)
: ? "transit_rewrap_latest_version_option_text"
"transit_rewrap_version_option_text"; : "transit_rewrap_version_option_text";
return { return {
version: String(val), version: String(val),
label: i18next.t(i18nkey, { version_num: String(val) }), label: i18next.t(i18nkey, { version_num: String(val) }),
} };
}) });
setPageContent(""); setPageContent("");
this.transitRewrapForm = makeElement({ this.transitRewrapForm = makeElement({
@ -53,20 +55,22 @@ export class TransitRewrapPage extends Page {
tag: "select", tag: "select",
name: "version", name: "version",
class: ["uk-select", "uk-width-1-2"], 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(
tag: "textarea", makeElement({
class: ["uk-textarea", "uk-width-1-2"], tag: "textarea",
attributes: { class: ["uk-textarea", "uk-width-1-2"],
placeholder: i18next.t("transit_rewrap_input_placeholder"), attributes: {
name: "ciphertext", placeholder: i18next.t("transit_rewrap_input_placeholder"),
} name: "ciphertext",
})), },
}),
),
makeElement({ makeElement({
tag: "p", tag: "p",
id: "errorText", id: "errorText",
class: "uk-text-danger" class: "uk-text-danger",
}), }),
makeElement({ makeElement({
tag: "button", tag: "button",
@ -74,9 +78,9 @@ export class TransitRewrapPage extends Page {
text: i18next.t("transit_rewrap_rewrap_btn"), text: i18next.t("transit_rewrap_rewrap_btn"),
attributes: { attributes: {
type: "submit", type: "submit",
} },
}) }),
] ],
}) as HTMLFormElement; }) as HTMLFormElement;
setPageContent(this.transitRewrapForm); setPageContent(this.transitRewrapForm);
@ -89,14 +93,10 @@ export class TransitRewrapPage extends Page {
async transitRewrapFormHandler(): Promise<void> { async transitRewrapFormHandler(): Promise<void> {
const formData = new FormData(this.transitRewrapForm); const formData = new FormData(this.transitRewrapForm);
try { try {
const res = await transitRewrap( const res = await transitRewrap(pageState.currentBaseMount, pageState.currentSecret, {
pageState.currentBaseMount, ciphertext: formData.get("ciphertext") as string,
pageState.currentSecret, key_version: parseInt(formData.get("version") as string, 10),
{ });
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); const modal = CopyableModal(i18next.t("transit_rewrap_result_modal_title"), res.ciphertext);
document.body.querySelector("#pageContent").appendChild(modal); document.body.querySelector("#pageContent").appendChild(modal);
modal.show(); modal.show();

View file

@ -4,7 +4,7 @@ import { changePage, setErrorText, setPageContent, setTitleElement } from "../..
import { getTransitKeys } from "../../api/transit/getTransitKeys"; import { getTransitKeys } from "../../api/transit/getTransitKeys";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
export class TransitViewPage extends Page { export class TransitViewPage extends Page {
constructor() { constructor() {
@ -29,39 +29,43 @@ export class TransitViewPage extends Page {
class: ["uk-button", "uk-button-primary", "uk-margin-bottom"], class: ["uk-button", "uk-button-primary", "uk-margin-bottom"],
onclick: () => { onclick: () => {
changePage("TRANSIT_NEW_KEY"); changePage("TRANSIT_NEW_KEY");
} },
}); });
transitViewContent.appendChild(newButton); transitViewContent.appendChild(newButton);
try { try {
const res = await getTransitKeys(pageState.currentBaseMount); const res = await getTransitKeys(pageState.currentBaseMount);
transitViewContent.appendChild(makeElement({ transitViewContent.appendChild(
tag: "ul", makeElement({
class: ["uk-nav", "uk-nav-default"], tag: "ul",
children: [ class: ["uk-nav", "uk-nav-default"],
...res.map(function (secret) { children: [
return makeElement({ ...res.map(function (secret) {
tag: "li", return makeElement({
children: makeElement({ tag: "li",
tag: "a", children: makeElement({
text: secret, tag: "a",
onclick: () => { text: secret,
pageState.currentSecret = secret; onclick: () => {
changePage("TRANSIT_VIEW_SECRET"); pageState.currentSecret = secret;
} changePage("TRANSIT_VIEW_SECRET");
}) },
}); }),
}) });
] }),
})); ],
}),
);
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error; const error = e as Error;
if (error == DoesNotExistError) { if (error == DoesNotExistError) {
transitViewContent.appendChild(makeElement({ transitViewContent.appendChild(
tag: "p", makeElement({
text: i18next.t("transit_view_none_here_text") tag: "p",
})); text: i18next.t("transit_view_none_here_text"),
}),
);
} else { } else {
setErrorText(error.message); setErrorText(error.message);
} }

View file

@ -4,7 +4,7 @@ import { changePage, setPageContent, setTitleElement } from "../../pageUtils";
import { getTransitKey } from "../../api/transit/getTransitKey"; import { getTransitKey } from "../../api/transit/getTransitKey";
import { makeElement } from "../../htmlUtils"; import { makeElement } from "../../htmlUtils";
import { pageState } from "../../globalPageState"; import { pageState } from "../../globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
export class TransitViewSecretPage extends Page { export class TransitViewSecretPage extends Page {
constructor() { constructor() {
@ -20,37 +20,45 @@ export class TransitViewSecretPage extends Page {
const transitKey = await getTransitKey(pageState.currentBaseMount, pageState.currentSecret); const transitKey = await getTransitKey(pageState.currentBaseMount, pageState.currentSecret);
setPageContent(makeElement({ setPageContent(
tag: "div", makeElement({
class: "uk-child-width-1-1@s uk-child-width-1-2@m uk-grid-small uk-grid-match", tag: "div",
attributes: { "uk-grid": "" }, class: "uk-child-width-1-1@s uk-child-width-1-2@m uk-grid-small uk-grid-match",
children: [ attributes: { "uk-grid": "" },
Tile({ children: [
condition: transitKey.supports_encryption, Tile({
title: i18next.t("transit_view_encrypt_text"), condition: transitKey.supports_encryption,
description: i18next.t("transit_view_encrypt_description"), title: i18next.t("transit_view_encrypt_text"),
icon: "lock", description: i18next.t("transit_view_encrypt_description"),
iconText: i18next.t("transit_view_encrypt_icon_text"), icon: "lock",
onclick: () => { changePage("TRANSIT_ENCRYPT"); } iconText: i18next.t("transit_view_encrypt_icon_text"),
}), onclick: () => {
Tile({ changePage("TRANSIT_ENCRYPT");
condition: transitKey.supports_decryption, },
title: i18next.t("transit_view_decrypt_text"), }),
description: i18next.t("transit_view_decrypt_description"), Tile({
icon: "mail", condition: transitKey.supports_decryption,
iconText: i18next.t("transit_view_decrypt_icon_text"), title: i18next.t("transit_view_decrypt_text"),
onclick: () => { changePage("TRANSIT_DECRYPT"); } description: i18next.t("transit_view_decrypt_description"),
}), icon: "mail",
Tile({ iconText: i18next.t("transit_view_decrypt_icon_text"),
condition: transitKey.supports_decryption, onclick: () => {
title: i18next.t("transit_view_rewrap_text"), changePage("TRANSIT_DECRYPT");
description: i18next.t("transit_view_rewrap_description"), },
icon: "code", }),
iconText: i18next.t("transit_view_rewrap_icon_text"), Tile({
onclick: () => { changePage("TRANSIT_REWRAP"); } condition: transitKey.supports_decryption,
}), title: i18next.t("transit_view_rewrap_text"),
] description: i18next.t("transit_view_rewrap_description"),
})); icon: "code",
iconText: i18next.t("transit_view_rewrap_icon_text"),
onclick: () => {
changePage("TRANSIT_REWRAP");
},
}),
],
}),
);
} }
get name(): string { get name(): string {

View file

@ -5,13 +5,12 @@ import { SealStatusType, getSealStatus } from "../api/sys/getSealStatus";
import { changePage, setErrorText, setPageContent } from "../pageUtils"; import { changePage, setErrorText, setPageContent } from "../pageUtils";
import { makeElement } from "../htmlUtils"; import { makeElement } from "../htmlUtils";
import { submitUnsealKey } from "../api/sys/submitUnsealKey"; import { submitUnsealKey } from "../api/sys/submitUnsealKey";
import i18next from 'i18next'; import i18next from "i18next";
const UnsealInputModes = { const UnsealInputModes = {
FORM_INPUT: "FORM_INPUT", FORM_INPUT: "FORM_INPUT",
QR_INPUT: "QR_INPUT" QR_INPUT: "QR_INPUT",
} };
export class UnsealPage extends Page { export class UnsealPage extends Page {
constructor() { constructor() {
@ -57,28 +56,33 @@ export class UnsealPage extends Page {
this.unsealProgress = makeElement({ this.unsealProgress = makeElement({
tag: "progress", tag: "progress",
class: "uk-progress", class: "uk-progress",
attributes: { value: "0", max: "0" } attributes: { value: "0", max: "0" },
}) as HTMLProgressElement; }) as HTMLProgressElement;
this.unsealProgressText = makeElement({ this.unsealProgressText = makeElement({
tag: "p", 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; }) as HTMLParagraphElement;
this.unsealInputContent = makeElement({ this.unsealInputContent = makeElement({
tag: "div"
});
setPageContent(makeElement({
tag: "div", tag: "div",
children: [ });
this.unsealProgress, setPageContent(
makeElement({ makeElement({
tag: "p", tag: "div",
id: "errorText", children: [
class: ["uk-text-danger", "uk-margin-top"] this.unsealProgress,
}), makeElement({
this.unsealProgressText, tag: "p",
this.unsealInputContent id: "errorText",
] class: ["uk-text-danger", "uk-margin-top"],
})); }),
this.unsealProgressText,
this.unsealInputContent,
],
}),
);
this.switchInputMode(this.mode); this.switchInputMode(this.mode);
this.updateSealProgress(await getSealStatus()); this.updateSealProgress(await getSealStatus());
this.makeRefresher(); this.makeRefresher();
@ -86,29 +90,28 @@ export class UnsealPage extends Page {
setButtons(method: string): void { setButtons(method: string): void {
const newMethod: string = const newMethod: string =
method == UnsealInputModes.FORM_INPUT ? method == UnsealInputModes.FORM_INPUT
UnsealInputModes.QR_INPUT ? UnsealInputModes.QR_INPUT
: : UnsealInputModes.FORM_INPUT;
UnsealInputModes.FORM_INPUT;
const buttonText: string = const buttonText: string =
newMethod == UnsealInputModes.FORM_INPUT ? newMethod == UnsealInputModes.FORM_INPUT
i18next.t("unseal_input_btn") ? i18next.t("unseal_input_btn")
: : i18next.t("unseal_qr_btn");
i18next.t("unseal_qr_btn"); this.unsealInputContent.appendChild(
this.unsealInputContent.appendChild(makeElement({ makeElement({
tag: "button", tag: "button",
class: ["uk-button", "uk-button-primary"], class: ["uk-button", "uk-button-primary"],
text: buttonText, text: buttonText,
onclick: () => { onclick: () => {
this.switchInputMode(newMethod); this.switchInputMode(newMethod);
} },
})) }),
);
} }
switchInputMode(method: string): void { switchInputMode(method: string): void {
this.deinitWebcam(); 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.FORM_INPUT) this.makeUnsealForm();
if (method == UnsealInputModes.QR_INPUT) void this.makeQRInput(); if (method == UnsealInputModes.QR_INPUT) void this.makeQRInput();
this.setButtons(method); this.setButtons(method);
@ -118,22 +121,26 @@ export class UnsealPage extends Page {
this.unsealKeyForm = makeElement({ this.unsealKeyForm = makeElement({
tag: "form", tag: "form",
children: [ children: [
MarginInline(makeElement({ MarginInline(
tag: "input", makeElement({
class: ["uk-input", "uk-form-width-medium"], tag: "input",
attributes: { class: ["uk-input", "uk-form-width-medium"],
required: "true", attributes: {
type: "password", required: "true",
placeholder: i18next.t("key_input_placeholder"), type: "password",
name: "key" placeholder: i18next.t("key_input_placeholder"),
} name: "key",
})), },
MarginInline(makeElement({ }),
tag: "button", ),
class: ["uk-button", "uk-button-primary"], MarginInline(
text: i18next.t("submit_key_btn") makeElement({
})), tag: "button",
] class: ["uk-button", "uk-button-primary"],
text: i18next.t("submit_key_btn"),
}),
),
],
}) as HTMLFormElement; }) as HTMLFormElement;
this.unsealInputContent.appendChild(this.unsealKeyForm); this.unsealInputContent.appendChild(this.unsealKeyForm);
this.unsealKeyForm.addEventListener("submit", (e: Event) => { this.unsealKeyForm.addEventListener("submit", (e: Event) => {
@ -155,7 +162,7 @@ export class UnsealPage extends Page {
const text = this.unsealProgressText; const text = this.unsealProgressText;
text.innerText = i18next.t("unseal_keys_progress", { text.innerText = i18next.t("unseal_keys_progress", {
progress: String(progress), progress: String(progress),
keys_needed: String(keysNeeded) keys_needed: String(keysNeeded),
}); });
const progressBar = this.unsealProgress; const progressBar = this.unsealProgress;
progressBar.value = progress; progressBar.value = progress;
@ -167,19 +174,21 @@ export class UnsealPage extends Page {
} }
submitKey(key: string): void { submitKey(key: string): void {
submitUnsealKey(key).then(_ => { submitUnsealKey(key)
void getSealStatus().then(data => { .then((_) => {
void this.updateSealProgress(data); void getSealStatus().then((data) => {
void this.updateSealProgress(data);
});
})
.catch((e: Error) => {
setErrorText(e.message);
}); });
}).catch((e: Error) => {
setErrorText(e.message);
});
} }
handleKeySubmit(): void { handleKeySubmit(): void {
const formData = new FormData(this.unsealKeyForm); const formData = new FormData(this.unsealKeyForm);
this.submitKey(formData.get("key") as string) this.submitKey(formData.get("key") as string);
} }
get name(): string { get name(): string {
return i18next.t("unseal_vault_text"); return i18next.t("unseal_vault_text");

View file

@ -1,6 +1,6 @@
import { PageState } from "./PageState"; import { PageState } from "./PageState";
import { pageState } from "./globalPageState"; import { pageState } from "./globalPageState";
import i18next from 'i18next'; import i18next from "i18next";
// Playground is a way to debug and test things. // Playground is a way to debug and test things.
// Anything you put in here is gonna be run on page initial load // 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" // Also it only runs when process.env.NODE_ENV == "development"
declare global { declare global {
interface Window { pageState: PageState; i18next: unknown; } interface Window {
pageState: PageState;
i18next: unknown;
}
} }
// Please empty this function before committing. // Please empty this function before committing.

View file

@ -1,25 +1,24 @@
module.exports = { module.exports = {
// The localised name for the language // The localised name for the language
"language_name": "Deutsche", language_name: "Deutsche",
// Internal: The direction of text (ltr or rtl) // Internal: The direction of text (ltr or rtl)
"language_direction": "ltr", language_direction: "ltr",
// These are the buttons on the top bar. // These are the buttons on the top bar.
"home_btn": "Startseite", home_btn: "Startseite",
"back_btn": "Zurück", back_btn: "Zurück",
"refresh_btn": "Neu laden", refresh_btn: "Neu laden",
"me_btn": "Profil/Einstellungen", me_btn: "Profil/Einstellungen",
// These are the page titles // These are the page titles
"me_page_title": "Proil/Einstellungen", me_page_title: "Proil/Einstellungen",
"home_page_title": "Startseite", home_page_title: "Startseite",
// These are all o the other translations // These are all o the other translations
"log_out_btn": "Abmelden", log_out_btn: "Abmelden",
"copy_token_btn": "Token kopieren", copy_token_btn: "Token kopieren",
"renew_lease_btn": "Token erneuern", renew_lease_btn: "Token erneuern",
"vaulturl_text": "Tresor-URL: {{text}}", vaulturl_text: "Tresor-URL: {{text}}",
"password_generator_btn": "Passwortgenerator", password_generator_btn: "Passwortgenerator",
"your_token_expires_in": "Ihr Token läuft in {{date, until_date}} ab" your_token_expires_in: "Ihr Token läuft in {{date, until_date}} ab",
};
}

230
src/translations/en.js vendored
View file

@ -1,164 +1,168 @@
module.exports = { module.exports = {
// The localised name for the language // The localised name for the language
"language_name": "English", language_name: "English",
// Internal: The direction of text (ltr or rtl) // Internal: The direction of text (ltr or rtl)
"language_direction": "ltr", language_direction: "ltr",
// These are the buttons on the top bar. // These are the buttons on the top bar.
"home_btn": "Home", home_btn: "Home",
"back_btn": "Back", back_btn: "Back",
"refresh_btn": "Refresh", refresh_btn: "Refresh",
"me_btn": "Me/Settings", me_btn: "Me/Settings",
// General Notification Messages // General Notification Messages
"notification_copy_success": "Copied to clipboard.", notification_copy_success: "Copied to clipboard.",
// Copyable Modal // Copyable Modal
"copy_modal_download_btn": "Download", copy_modal_download_btn: "Download",
"copy_modal_copy_btn": "Copy", copy_modal_copy_btn: "Copy",
"copy_modal_close_btn": "Close", copy_modal_close_btn: "Close",
// Copyable Input Box // 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
"file_upload_input_btn": "Upload File", file_upload_input_btn: "Upload File",
// Me Page // Me Page
"me_page_title": "Me/Settings", me_page_title: "Me/Settings",
"log_out_btn": "Log Out", log_out_btn: "Log Out",
"seal_vault_btn": "Seal Vault", seal_vault_btn: "Seal Vault",
"copy_token_btn": "Copy Token", copy_token_btn: "Copy Token",
"renew_lease_btn": "Renew Token Lease", renew_lease_btn: "Renew Token Lease",
"change_language_btn": "Change Language", change_language_btn: "Change Language",
// Home Page // Home Page
"home_page_title": "Home", home_page_title: "Home",
"vaulturl_text": "Vault URL: {{text}}", vaulturl_text: "Vault URL: {{text}}",
"password_generator_btn": "Password Generator", password_generator_btn: "Password Generator",
"your_token_expires_in": "Your token expires in {{date, until_date}}", your_token_expires_in: "Your token expires in {{date, until_date}}",
// Unseal Page // Unseal Page
"unseal_vault_text": "Unseal the Vault", unseal_vault_text: "Unseal the Vault",
"submit_key_btn": "Submit Key", submit_key_btn: "Submit Key",
"unseal_input_btn": "Switch to Manual Key Input", unseal_input_btn: "Switch to Manual Key Input",
"unseal_qr_btn": "Switch to QR Key Input", unseal_qr_btn: "Switch to QR Key Input",
"key_input_placeholder": "Key", key_input_placeholder: "Key",
"unseal_keys_progress": "Keys: {{progress}}/{{keys_needed}}", unseal_keys_progress: "Keys: {{progress}}/{{keys_needed}}",
// Language Selector Page // Language Selector Page
"set_language_title": "Set Language", set_language_title: "Set Language",
"set_language_btn": "Set Language", set_language_btn: "Set Language",
// Password Generator Page // Password Generator Page
"password_generator_title": "Password Generator", password_generator_title: "Password Generator",
"password_length_title": "Password Length ({{min}}/{{max}})", password_length_title: "Password Length ({{min}}/{{max}})",
"gen_password_btn": "Generate Password", gen_password_btn: "Generate Password",
// Login Page // Login Page
"log_in_title": "Login", log_in_title: "Login",
"log_in_with_token": "Token", log_in_with_token: "Token",
"log_in_with_username": "Username", log_in_with_username: "Username",
"token_input": "Token", token_input: "Token",
"username_input": "Username", username_input: "Username",
"password_input": "Password", password_input: "Password",
"log_in_btn": "Login", log_in_btn: "Login",
"token_login_error": "Invalid Token", token_login_error: "Invalid Token",
// Key Value Delete Page // Key Value Delete Page
"kv_delete_title": "K/V Delete", kv_delete_title: "K/V Delete",
"kv_delete_text": "Are you sure you want to delete this?", kv_delete_text: "Are you sure you want to delete this?",
"kv_delete_btn": "Delete", kv_delete_btn: "Delete",
"kv_delete_suffix": " (delete)", kv_delete_suffix: " (delete)",
// Key Value New Page // Key Value New Page
"kv_new_title": "K/V New", kv_new_title: "K/V New",
"kv_new_suffix": " (new)", kv_new_suffix: " (new)",
"kv_new_path": "Relative Path", kv_new_path: "Relative Path",
"kv_new_create_btn": "Create Empty Secret", kv_new_create_btn: "Create Empty Secret",
// Key Value Secret Page // Key Value Secret Page
"kv_secret_title": "K/V Secret", 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_deleted_text:
"kv_secret_restore_btn": "Restore Secret Version", "This secret version has been soft deleted but remains restorable, do you want to restore it?",
"kv_secret_loading": "Loading Secret..", kv_secret_restore_btn: "Restore Secret Version",
"kv_secret_delete_btn": "Delete", kv_secret_loading: "Loading Secret..",
"kv_secret_delete_all_btn": "Delete All Versions", kv_secret_delete_btn: "Delete",
"kv_secret_delete_version_btn": "Delete Version {{ version }}", kv_secret_delete_all_btn: "Delete All Versions",
"kv_secret_edit_btn": "Edit", kv_secret_delete_version_btn: "Delete Version {{ version }}",
"kv_secret_versions_btn": "Versions", kv_secret_edit_btn: "Edit",
kv_secret_versions_btn: "Versions",
// Key Value Secret Editor Page // Key Value Secret Editor Page
"kv_sec_edit_title": "K/V Edit", kv_sec_edit_title: "K/V Edit",
"kv_sec_edit_btn": "Edit", kv_sec_edit_btn: "Edit",
"kv_sec_edit_loading": "Loading Editor..", kv_sec_edit_loading: "Loading Editor..",
"kv_sec_edit_invalid_json_err": "Invalid JSON", kv_sec_edit_invalid_json_err: "Invalid JSON",
"kv_sec_edit_suffix": " (edit)", kv_sec_edit_suffix: " (edit)",
// Key Value Secret Versions Page // Key Value Secret Versions Page
"kv_sec_versions_title": "K/V Versions", kv_sec_versions_title: "K/V Versions",
"kv_sec_versions_suffix": " (versions)", kv_sec_versions_suffix: " (versions)",
// Key Value View/List Secrets Page // Key Value View/List Secrets Page
"kv_view_title": "K/V View", 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_cubbyhole_text:
"kv_view_new_btn": "New", "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_none_here_text": "You seem to have no secrets here, would you like to create one?", 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 Page
"totp_view_title": "TOTP", totp_view_title: "TOTP",
"totp_view_new_btn": "Add new TOTP key", totp_view_new_btn: "Add new TOTP key",
"totp_view_loading": "Loading TOTP Codes..", 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_empty: "You seem to have no TOTP codes here, would you like to create one?",
"totp_view_loading_box": "Loading..", totp_view_loading_box: "Loading..",
// New TOTP Key Page // New TOTP Key Page
"totp_new_title": "New TOTP Key", totp_new_title: "New TOTP Key",
"totp_new_suffix": " (new)", totp_new_suffix: " (new)",
"totp_new_name_text": "TOTP Key Name", 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_info:
"totp_new_uri_input": "URI", "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_key_input": "Key", totp_new_uri_input: "URI",
"totp_new_add_btn": "Add TOTP Key", totp_new_key_input: "Key",
totp_new_add_btn: "Add TOTP Key",
// Transit View Page // Transit View Page
"transit_view_title": "Transit View", 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_none_here_text:
"You seem to have no transit keys here, would you like to create one?",
// Transit View Secret Page // Transit View Secret Page
"transit_view_secret_title": "Transit Secret View", transit_view_secret_title: "Transit Secret View",
"transit_view_encrypt_text": "Encrypt", transit_view_encrypt_text: "Encrypt",
"transit_view_encrypt_icon_text": "Encryption Icon", transit_view_encrypt_icon_text: "Encryption Icon",
"transit_view_encrypt_description": "Encrypt some plaintext or base64 encoded binary.", transit_view_encrypt_description: "Encrypt some plaintext or base64 encoded binary.",
"transit_view_decrypt_text": "Decrypt", transit_view_decrypt_text: "Decrypt",
"transit_view_decrypt_description": "Decrypt some cyphertext.", transit_view_decrypt_description: "Decrypt some cyphertext.",
"transit_view_decrypt_icon_text": "Decryption Icon", transit_view_decrypt_icon_text: "Decryption Icon",
"transit_view_rewrap_text": "Rewrap", transit_view_rewrap_text: "Rewrap",
"transit_view_rewrap_description": "Rewrap ciphertext using a different key version.", transit_view_rewrap_description: "Rewrap ciphertext using a different key version.",
"transit_view_rewrap_icon_text": "Rewrap Icon", transit_view_rewrap_icon_text: "Rewrap Icon",
// Transit Encrypt Page // Transit Encrypt Page
"transit_encrypt_title": "Transit Encrypt", transit_encrypt_title: "Transit Encrypt",
"transit_encrypt_suffix": " (encrypt)", transit_encrypt_suffix: " (encrypt)",
"transit_encrypt_input_placeholder": "Plaintext or base64", transit_encrypt_input_placeholder: "Plaintext or base64",
"transit_encrypt_already_encoded_checkbox": "Is the data already encoded in base64?", transit_encrypt_already_encoded_checkbox: "Is the data already encoded in base64?",
"transit_encrypt_encrypt_btn": "Encrypt", transit_encrypt_encrypt_btn: "Encrypt",
"transit_encrypt_encryption_result_modal_title": "Encryption Result", transit_encrypt_encryption_result_modal_title: "Encryption Result",
// Transit Decrypt Page // Transit Decrypt Page
"transit_decrypt_title": "Transit Decrypt", transit_decrypt_title: "Transit Decrypt",
"transit_decrypt_suffix": " (decrypt)", transit_decrypt_suffix: " (decrypt)",
"transit_decrypt_input_placeholder": "Cyphertext", transit_decrypt_input_placeholder: "Cyphertext",
"transit_decrypt_decode_checkbox": "Should the plaintext be base64 decoded?", transit_decrypt_decode_checkbox: "Should the plaintext be base64 decoded?",
"transit_decrypt_decrypt_btn": "Decrypt", transit_decrypt_decrypt_btn: "Decrypt",
"transit_decrypt_decryption_result_modal_title": "Decryption Result", transit_decrypt_decryption_result_modal_title: "Decryption Result",
// Transit Rewrap Page // Transit Rewrap Page
"transit_rewrap_title": "Transit Rewrap", transit_rewrap_title: "Transit Rewrap",
"transit_rewrap_suffix": " (rewrap)", transit_rewrap_suffix: " (rewrap)",
"transit_rewrap_version_option_text": "{{version_num}}", transit_rewrap_version_option_text: "{{version_num}}",
"transit_rewrap_latest_version_option_text": "{{version_num}} (latest)", transit_rewrap_latest_version_option_text: "{{version_num}} (latest)",
"transit_rewrap_input_placeholder": "Cyphertext", transit_rewrap_input_placeholder: "Cyphertext",
"transit_rewrap_rewrap_btn": "Rewrap", transit_rewrap_rewrap_btn: "Rewrap",
"transit_rewrap_result_modal_title": "Rewrap Result", transit_rewrap_result_modal_title: "Rewrap Result",
} };

205
src/translations/fr.js vendored
View file

@ -1,147 +1,152 @@
module.exports = { module.exports = {
// The localised name for the language // The localised name for the language
"language_name": "Français", language_name: "Français",
// Internal: The direction of text (ltr or rtl) // Internal: The direction of text (ltr or rtl)
"language_direction": "ltr", language_direction: "ltr",
// These are the buttons on the top bar. // These are the buttons on the top bar.
"home_btn": "Accueil", home_btn: "Accueil",
"back_btn": "Retour", back_btn: "Retour",
"refresh_btn": "Rafraichir", refresh_btn: "Rafraichir",
"me_btn": "Profil/Paramètres", me_btn: "Profil/Paramètres",
// General Notification Messages // General Notification Messages
"notification_copy_success": "Copié dans le presse-papiers.", notification_copy_success: "Copié dans le presse-papiers.",
// Copyable Modal // Copyable Modal
"copy_modal_download_btn": "Télécharger", copy_modal_download_btn: "Télécharger",
"copy_modal_copy_btn": "Copier", copy_modal_copy_btn: "Copier",
"copy_modal_close_btn": "Fermer", copy_modal_close_btn: "Fermer",
// Copyable Input Box // Copyable Input Box
"copy_input_box_copy_icon_text": "Bouton Copier", copy_input_box_copy_icon_text: "Bouton Copier",
// Me Page // Me Page
"me_page_title": "Profil/Paramètres", me_page_title: "Profil/Paramètres",
"log_out_btn": "Déconnexion", log_out_btn: "Déconnexion",
"seal_vault_btn": "Verrouiller le coffre", seal_vault_btn: "Verrouiller le coffre",
"copy_token_btn": "Copier le jeton", copy_token_btn: "Copier le jeton",
"renew_lease_btn": "Renouveler le jeton", renew_lease_btn: "Renouveler le jeton",
"change_language_btn": "Changer la langue", change_language_btn: "Changer la langue",
// Home Page // Home Page
"home_page_title": "Accueil", home_page_title: "Accueil",
"vaulturl_text": "Adresse du Vault: {{text}}", vaulturl_text: "Adresse du Vault: {{text}}",
"password_generator_btn": "Générateur de mot de passe", password_generator_btn: "Générateur de mot de passe",
"your_token_expires_in": "Votre jeton expire dans {{date, until_date}}", your_token_expires_in: "Votre jeton expire dans {{date, until_date}}",
// Unseal Page // Unseal Page
"unseal_vault_text": "Ouvrir le Vault", unseal_vault_text: "Ouvrir le Vault",
"submit_key_btn": "Ajouter la clé", submit_key_btn: "Ajouter la clé",
"unseal_input_btn": "Basculer en entrée de clé manuelle", unseal_input_btn: "Basculer en entrée de clé manuelle",
"unseal_qr_btn": "Basculer en entrée de QR code", unseal_qr_btn: "Basculer en entrée de QR code",
"key_input_placeholder": "Clé", key_input_placeholder: "Clé",
"unseal_keys_progress": "Clés: {{progress}}/{{keys_needed}}", unseal_keys_progress: "Clés: {{progress}}/{{keys_needed}}",
// Language Selector Page // Language Selector Page
"set_language_title": "Langue", set_language_title: "Langue",
"set_language_btn": "Changer la langue", set_language_btn: "Changer la langue",
// Password Generator Page // Password Generator Page
"password_generator_title": "Générateur de mot de passe", password_generator_title: "Générateur de mot de passe",
"gen_password_btn": "Générer le mot de passe", gen_password_btn: "Générer le mot de passe",
// Login Page // Login Page
"log_in_title": "Connexion", log_in_title: "Connexion",
"log_in_with_token": "Jeton", log_in_with_token: "Jeton",
"log_in_with_username": "Nom d'utilisateur", log_in_with_username: "Nom d'utilisateur",
"token_input": "Jeton", token_input: "Jeton",
"username_input": "Nom d'utilisateur", username_input: "Nom d'utilisateur",
"password_input": "Mot de passe", password_input: "Mot de passe",
"log_in_btn": "Se connecter", log_in_btn: "Se connecter",
// Key Value Delete Page // Key Value Delete Page
"kv_delete_title": "Suppression clé/valeur", kv_delete_title: "Suppression clé/valeur",
"kv_delete_text": "Voulez-vous vraiment supprimer ceci ?", kv_delete_text: "Voulez-vous vraiment supprimer ceci ?",
"kv_delete_btn": "Supprimer", kv_delete_btn: "Supprimer",
"kv_delete_suffix": " (supprimé)", kv_delete_suffix: " (supprimé)",
// Key Value New Page // Key Value New Page
"kv_new_title": "Nouvelle clé/valeur", kv_new_title: "Nouvelle clé/valeur",
"kv_new_suffix": " (nouveau)", kv_new_suffix: " (nouveau)",
"kv_new_path": "Chemin relatif", kv_new_path: "Chemin relatif",
"kv_new_create_btn": "Créer un secret vide", kv_new_create_btn: "Créer un secret vide",
// Key Value Secret Page // Key Value Secret Page
"kv_secret_title": "Clé/valeur secrète", 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_deleted_text:
"kv_secret_restore_btn": "Restaurer la version secrète", "Cette version secrète a été supprimée temporairement mais reste restaurable, voulez-vous la restaurer ?",
"kv_secret_loading": "Chargement du secret..", kv_secret_restore_btn: "Restaurer la version secrète",
"kv_secret_delete_btn": "Supprimer", kv_secret_loading: "Chargement du secret..",
"kv_secret_delete_all_btn": "Supprimer toutes les versions", kv_secret_delete_btn: "Supprimer",
"kv_secret_delete_version_btn": "Supprimer la version {{ version }}", kv_secret_delete_all_btn: "Supprimer toutes les versions",
"kv_secret_edit_btn": "Éditer", kv_secret_delete_version_btn: "Supprimer la version {{ version }}",
"kv_secret_versions_btn": "Versions", kv_secret_edit_btn: "Éditer",
kv_secret_versions_btn: "Versions",
// Key Value Secret Editor Page // Key Value Secret Editor Page
"kv_sec_edit_title": "Édition clé/valeur", kv_sec_edit_title: "Édition clé/valeur",
"kv_sec_edit_btn": "Éditer", kv_sec_edit_btn: "Éditer",
"kv_sec_edit_loading": "Chargement de l'éditeur..", kv_sec_edit_loading: "Chargement de l'éditeur..",
"kv_sec_edit_invalid_json_err": "Code JSON invalide", kv_sec_edit_invalid_json_err: "Code JSON invalide",
"kv_sec_edit_suffix": " (édité)", kv_sec_edit_suffix: " (édité)",
// Key Value Secret Versions Page // Key Value Secret Versions Page
"kv_sec_versions_title": "Versions clé/valeur", kv_sec_versions_title: "Versions clé/valeur",
"kv_sec_versions_suffix": " (versions)", kv_sec_versions_suffix: " (versions)",
// Key Value View/List Secrets Page // Key Value View/List Secrets Page
"kv_view_title": "Visionneuse clé/valeur", 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_cubbyhole_text:
"kv_view_new_btn": "Nouveau", "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_none_here_text": "Vous semblez ne pas avoir de secrets ici, voulez-vous en créer un ?", 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 Page
"totp_view_title": "TOTP", totp_view_title: "TOTP",
"totp_view_new_btn": "Ajouter une clé TOTP", totp_view_new_btn: "Ajouter une clé TOTP",
"totp_view_loading": "Chargement des codes 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_empty: "Vous ne semblez pas avoir de code TOTP ici, voulez-vous en créer un ?",
"totp_view_loading_box": "Chargement..", totp_view_loading_box: "Chargement..",
// New TOTP Key Page // New TOTP Key Page
"totp_new_title": "Nouvelle clé TOTP", totp_new_title: "Nouvelle clé TOTP",
"totp_new_suffix": " (nouveau)", totp_new_suffix: " (nouveau)",
"totp_new_name_text": "Nom de clé TOTP", 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_info:
"totp_new_uri_input": "URI", "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_key_input": "Clé", totp_new_uri_input: "URI",
"totp_new_add_btn": "Ajouter clé TOTP", totp_new_key_input: "Clé",
totp_new_add_btn: "Ajouter clé TOTP",
// Transit View Page // Transit View Page
"transit_view_title": "Fenêtre Transit", 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_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 Page
"transit_view_secret_title": "Transit Secret", transit_view_secret_title: "Transit Secret",
"transit_view_encrypt_text": "Chiffrer", transit_view_encrypt_text: "Chiffrer",
"transit_view_encrypt_icon_text": "Icône chiffrement", 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_encrypt_description:
"transit_view_decrypt_text": "Déchiffrer", "Chiffrer du texte brut ou données binaires encodées en base64.",
"transit_view_decrypt_description": "Déchiffrer du texte chiffré.", transit_view_decrypt_text: "Déchiffrer",
"transit_view_decrypt_icon_text": "Icône déchiffrement", transit_view_decrypt_description: "Déchiffrer du texte chiffré.",
transit_view_decrypt_icon_text: "Icône déchiffrement",
// Transit Encrypt Page // Transit Encrypt Page
"transit_encrypt_title": "Chiffrement Transit", transit_encrypt_title: "Chiffrement Transit",
"transit_encrypt_suffix": " (chiffrer)", transit_encrypt_suffix: " (chiffrer)",
"transit_encrypt_input_placeholder": "Texte brut ou base64", 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_already_encoded_checkbox: "Les données sont-elles déjà encodées en base64 ?",
"transit_encrypt_encrypt_btn": "Chiffrer", transit_encrypt_encrypt_btn: "Chiffrer",
"transit_encrypt_encryption_result_modal_title": "Résultat chiffré", transit_encrypt_encryption_result_modal_title: "Résultat chiffré",
// Transit decrypt Page // Transit decrypt Page
"transit_decrypt_title": "Déchiffrement Transit", transit_decrypt_title: "Déchiffrement Transit",
"transit_decrypt_suffix": " (déchiffrer)", transit_decrypt_suffix: " (déchiffrer)",
"transit_decrypt_input_placeholder": "Message chiffré", transit_decrypt_input_placeholder: "Message chiffré",
"transit_decrypt_decode_checkbox": "Est-ce que le texte brut doit être encodé en base64 ?", transit_decrypt_decode_checkbox: "Est-ce que le texte brut doit être encodé en base64 ?",
"transit_decrypt_decrypt_btn": "Déchiffrer", transit_decrypt_decrypt_btn: "Déchiffrer",
"transit_decrypt_decryption_result_modal_title": "Résultat déchiffré", transit_decrypt_decryption_result_modal_title: "Résultat déchiffré",
} };

View file

@ -1,24 +1,24 @@
module.exports = { module.exports = {
// The localised name for the language // The localised name for the language
"language_name": "Nederlands", language_name: "Nederlands",
// Internal: The direction of text (ltr or rtl) // Internal: The direction of text (ltr or rtl)
"language_direction": "ltr", language_direction: "ltr",
// These are the buttons on the top bar. // These are the buttons on the top bar.
"home_btn": "Homepagina", home_btn: "Homepagina",
"back_btn": "Terug", back_btn: "Terug",
"refresh_btn": "Ververs", refresh_btn: "Ververs",
"me_btn": "Profiel/Instellingen", me_btn: "Profiel/Instellingen",
// These are the page titles // These are the page titles
"me_page_title": "Profiel/Instellingen", me_page_title: "Profiel/Instellingen",
"home_page_title": "Homepagina", home_page_title: "Homepagina",
// These are all o the other translations // These are all o the other translations
"log_out_btn": "Log Uit", log_out_btn: "Log Uit",
"copy_token_btn": "Kopieer Token", copy_token_btn: "Kopieer Token",
"renew_lease_btn": "Vernieuw Token-lease", renew_lease_btn: "Vernieuw Token-lease",
"vaulturl_text": "Vault URL: {{text}}", vaulturl_text: "Vault URL: {{text}}",
"password_generator_btn": "Wachtwoordgenerator", password_generator_btn: "Wachtwoordgenerator",
"your_token_expires_in": "Uw token vervalt in {{date, until_date}}" your_token_expires_in: "Uw token vervalt in {{date, until_date}}",
} };

229
src/translations/ru.js vendored
View file

@ -1,164 +1,167 @@
module.exports = { module.exports = {
// The localised name for the language // The localised name for the language
"language_name": "русский", language_name: "русский",
// Internal: The direction of text (ltr or rtl) // Internal: The direction of text (ltr or rtl)
"language_direction": "ltr", language_direction: "ltr",
// These are the buttons on the top bar. // These are the buttons on the top bar.
"home_btn": "Главная страница", home_btn: "Главная страница",
"back_btn": "Назад", back_btn: "Назад",
"refresh_btn": "Обновить", refresh_btn: "Обновить",
"me_btn": "Профиль/параметры", me_btn: "Профиль/параметры",
// General Notification Messages // General Notification Messages
"notification_copy_success": "Текст скопирован в буфер обмена.", notification_copy_success: "Текст скопирован в буфер обмена.",
// Copyable Modal // Copyable Modal
"copy_modal_download_btn": "Загрузить", copy_modal_download_btn: "Загрузить",
"copy_modal_copy_btn": "Копировать", copy_modal_copy_btn: "Копировать",
"copy_modal_close_btn": "Закрыть", copy_modal_close_btn: "Закрыть",
// Copyable Input Box // Copyable Input Box
"copy_input_box_copy_icon_text": "Копировать", copy_input_box_copy_icon_text: "Копировать",
// File Upload Input // File Upload Input
"file_upload_input_btn": "Загрузить файл", file_upload_input_btn: "Загрузить файл",
// Me Page // Me Page
"me_page_title": "Профиль/параметры", me_page_title: "Профиль/параметры",
"log_out_btn": "Выход", log_out_btn: "Выход",
"seal_vault_btn": "Закрыть хранилище", seal_vault_btn: "Закрыть хранилище",
"copy_token_btn": "Копировать код доступа", copy_token_btn: "Копировать код доступа",
"renew_lease_btn": "Продлить действие кода доступа", renew_lease_btn: "Продлить действие кода доступа",
"change_language_btn": "Выбор языка", change_language_btn: "Выбор языка",
// Home Page // Home Page
"home_page_title": "Главная страница", home_page_title: "Главная страница",
"vaulturl_text": "Адрес хранилища: {{text}}", vaulturl_text: "Адрес хранилища: {{text}}",
"password_generator_btn": "Генератор паролей", password_generator_btn: "Генератор паролей",
"your_token_expires_in": "Продолжительность ключа: {{date, until_date}}", your_token_expires_in: "Продолжительность ключа: {{date, until_date}}",
// Unseal Page // Unseal Page
"unseal_vault_text": "Раскрыть хранилище", unseal_vault_text: "Раскрыть хранилище",
"submit_key_btn": "Отправить ключ", submit_key_btn: "Отправить ключ",
"unseal_input_btn": "Ввести ключ вручную", unseal_input_btn: "Ввести ключ вручную",
"unseal_qr_btn": "Ввести ключ через QR-код", unseal_qr_btn: "Ввести ключ через QR-код",
"key_input_placeholder": "Ключ", key_input_placeholder: "Ключ",
"unseal_keys_progress": "Ключи: {{progress}}/{{keys_needed}}", unseal_keys_progress: "Ключи: {{progress}}/{{keys_needed}}",
// Language Selector Page // Language Selector Page
"set_language_title": "Выбор языка", set_language_title: "Выбор языка",
"set_language_btn": "Выбрать язык", set_language_btn: "Выбрать язык",
// Password Generator Page // Password Generator Page
"password_generator_title": "Генератор паролей", password_generator_title: "Генератор паролей",
"password_length_title": "Длина пароля ({{min}}/{{max}})", password_length_title: "Длина пароля ({{min}}/{{max}})",
"gen_password_btn": "Генерировать", gen_password_btn: "Генерировать",
// Login Page // Login Page
"log_in_title": "Вход", log_in_title: "Вход",
"log_in_with_token": "Вход с кодом доступа", log_in_with_token: "Вход с кодом доступа",
"log_in_with_username": "Вход по имени", log_in_with_username: "Вход по имени",
"token_input": "Код доступа", token_input: "Код доступа",
"username_input": "Имя пользователя", username_input: "Имя пользователя",
"password_input": "Пароль", password_input: "Пароль",
"log_in_btn": "Войти", log_in_btn: "Войти",
"token_login_error": "Неправильный код доступа", token_login_error: "Неправильный код доступа",
// Key Value Delete Page // Key Value Delete Page
"kv_delete_title": "Удаление ключа/значения", kv_delete_title: "Удаление ключа/значения",
"kv_delete_text": "Вы уверены, что хотите удалить это?", kv_delete_text: "Вы уверены, что хотите удалить это?",
"kv_delete_btn": "Удалить", kv_delete_btn: "Удалить",
"kv_delete_suffix": " (удалить)", kv_delete_suffix: " (удалить)",
// Key Value New Page // Key Value New Page
"kv_new_title": "Новый ключ/значение", kv_new_title: "Новый ключ/значение",
"kv_new_suffix": " (новый)", kv_new_suffix: " (новый)",
"kv_new_path": "Относительный путь", kv_new_path: "Относительный путь",
"kv_new_create_btn": "Создать пустые тайные данные", kv_new_create_btn: "Создать пустые тайные данные",
// Key Value Secret Page // Key Value Secret Page
"kv_secret_title": "Тайные ключи/значения", kv_secret_title: "Тайные ключи/значения",
"kv_secret_deleted_text": "Тайная версия данных была удалена, но может быть восстановлена. Вы хотите восстановить её?", kv_secret_deleted_text:
"kv_secret_restore_btn": "Восстановить тайную версию", "Тайная версия данных была удалена, но может быть восстановлена. Вы хотите восстановить её?",
"kv_secret_loading": "Загрузка тайных данных..", kv_secret_restore_btn: "Восстановить тайную версию",
"kv_secret_delete_btn": "Удалить", kv_secret_loading: "Загрузка тайных данных..",
"kv_secret_delete_all_btn": "Удалить все версии", kv_secret_delete_btn: "Удалить",
"kv_secret_delete_version_btn": "Удалить версию {{ version }}", kv_secret_delete_all_btn: "Удалить все версии",
"kv_secret_edit_btn": "Редактировать", kv_secret_delete_version_btn: "Удалить версию {{ version }}",
"kv_secret_versions_btn": "Версии", kv_secret_edit_btn: "Редактировать",
kv_secret_versions_btn: "Версии",
// Key Value Secret Editor Page // Key Value Secret Editor Page
"kv_sec_edit_title": "Редактировать ключи/значения", kv_sec_edit_title: "Редактировать ключи/значения",
"kv_sec_edit_btn": "Редактировать", kv_sec_edit_btn: "Редактировать",
"kv_sec_edit_loading": "Загрузка редактора..", kv_sec_edit_loading: "Загрузка редактора..",
"kv_sec_edit_invalid_json_err": "Некорректный JSON", kv_sec_edit_invalid_json_err: "Некорректный JSON",
"kv_sec_edit_suffix": " (редакт.)", kv_sec_edit_suffix: " (редакт.)",
// Key Value Secret Versions Page // Key Value Secret Versions Page
"kv_sec_versions_title": "Версии ключей/значений", kv_sec_versions_title: "Версии ключей/значений",
"kv_sec_versions_suffix": " (версии)", kv_sec_versions_suffix: " (версии)",
// Key Value View/List Secrets Page // Key Value View/List Secrets Page
"kv_view_title": "Просмотр ключей/значений", kv_view_title: "Просмотр ключей/значений",
"kv_view_cubbyhole_text": "При использовании cubbyhole, тайные данные можно хранить до тех пор, пока действителен ваш код доступа. Они будут удалены при истечении кода и могут быть просмотрены только с использованием этого кода.", kv_view_cubbyhole_text:
"kv_view_new_btn": "Создать", "При использовании cubbyhole, тайные данные можно хранить до тех пор, пока действителен ваш код доступа. Они будут удалены при истечении кода и могут быть просмотрены только с использованием этого кода.",
"kv_view_none_here_text": "У вас на данный момент нет тайных данных. Хотите ли вы их создать?", kv_view_new_btn: "Создать",
kv_view_none_here_text: "У вас на данный момент нет тайных данных. Хотите ли вы их создать?",
// TOTP View Page // TOTP View Page
"totp_view_title": "TOTP", totp_view_title: "TOTP",
"totp_view_new_btn": "Добавить ключ TOTP", totp_view_new_btn: "Добавить ключ TOTP",
"totp_view_loading": "Загрузка кодов TOTP..", totp_view_loading: "Загрузка кодов TOTP..",
"totp_view_empty": "У вас на данный момент нет кодов TOTP. Хотите ли вы их создать?", totp_view_empty: "У вас на данный момент нет кодов TOTP. Хотите ли вы их создать?",
"totp_view_loading_box": "Загрузка..", totp_view_loading_box: "Загрузка..",
// New TOTP Key Page // New TOTP Key Page
"totp_new_title": "Новый ключ TOTP", totp_new_title: "Новый ключ TOTP",
"totp_new_suffix": " (новый)", totp_new_suffix: " (новый)",
"totp_new_name_text": "Имя ключа TOTP", totp_new_name_text: "Имя ключа TOTP",
"totp_new_info": "Необходим либо ключ, либо адрес URI. Лучше всего использовать URI, но он может не сработать. Отсканируйте QR-код и скопируйте его адрес.", totp_new_info:
"totp_new_uri_input": "URI", "Необходим либо ключ, либо адрес URI. Лучше всего использовать URI, но он может не сработать. Отсканируйте QR-код и скопируйте его адрес.",
"totp_new_key_input": "Ключ", totp_new_uri_input: "URI",
"totp_new_add_btn": "Добавить ключ TOTP", totp_new_key_input: "Ключ",
totp_new_add_btn: "Добавить ключ TOTP",
// Transit View Page // Transit View Page
"transit_view_title": "Ключи Transit", transit_view_title: "Ключи Transit",
"transit_view_none_here_text": "У вас нет ключей Transit, хотите ли вы их создать?", transit_view_none_here_text: "У вас нет ключей Transit, хотите ли вы их создать?",
// Transit View Secret Page // Transit View Secret Page
"transit_view_secret_title": "Просмотр тайных данных Transit", transit_view_secret_title: "Просмотр тайных данных Transit",
"transit_view_encrypt_text": "Зашифровать", transit_view_encrypt_text: "Зашифровать",
"transit_view_encrypt_icon_text": "Значок шифрования", transit_view_encrypt_icon_text: "Значок шифрования",
"transit_view_encrypt_description": "Зашифровать текст или двоичные данные, закодированные base64.", transit_view_encrypt_description: "Зашифровать текст или двоичные данные, закодированные base64.",
"transit_view_decrypt_text": "Расшифровать", transit_view_decrypt_text: "Расшифровать",
"transit_view_decrypt_description": "Расшифровать шифр-текст.", transit_view_decrypt_description: "Расшифровать шифр-текст.",
"transit_view_decrypt_icon_text": "Значок расшифрования", transit_view_decrypt_icon_text: "Значок расшифрования",
"transit_view_rewrap_text": "Перешифровать", transit_view_rewrap_text: "Перешифровать",
"transit_view_rewrap_description": "Перешифровать шифр-текст, используя другую версию ключа.", transit_view_rewrap_description: "Перешифровать шифр-текст, используя другую версию ключа.",
"transit_view_rewrap_icon_text": "Значок перешифрования", transit_view_rewrap_icon_text: "Значок перешифрования",
// Transit Encrypt Page // Transit Encrypt Page
"transit_encrypt_title": "Шифрование Transit", transit_encrypt_title: "Шифрование Transit",
"transit_encrypt_suffix": " (шифр.)", transit_encrypt_suffix: " (шифр.)",
"transit_encrypt_input_placeholder": "Текст или base64", transit_encrypt_input_placeholder: "Текст или base64",
"transit_encrypt_already_encoded_checkbox": "Данные уже закодированы в base64?", transit_encrypt_already_encoded_checkbox: "Данные уже закодированы в base64?",
"transit_encrypt_encrypt_btn": "Зашифровать", transit_encrypt_encrypt_btn: "Зашифровать",
"transit_encrypt_encryption_result_modal_title": "Результат шифрования", transit_encrypt_encryption_result_modal_title: "Результат шифрования",
// Transit Decrypt Page // Transit Decrypt Page
"transit_decrypt_title": "Расшифрование Transit", transit_decrypt_title: "Расшифрование Transit",
"transit_decrypt_suffix": " (расшифр.)", transit_decrypt_suffix: " (расшифр.)",
"transit_decrypt_input_placeholder": "Шифр-текст", transit_decrypt_input_placeholder: "Шифр-текст",
"transit_decrypt_decode_checkbox": "Нужно ли раскодировать текст из base64?", transit_decrypt_decode_checkbox: "Нужно ли раскодировать текст из base64?",
"transit_decrypt_decrypt_btn": "Расшифровать", transit_decrypt_decrypt_btn: "Расшифровать",
"transit_decrypt_decryption_result_modal_title": "Результат расшифрования", transit_decrypt_decryption_result_modal_title: "Результат расшифрования",
// Transit Rewrap Page // Transit Rewrap Page
"transit_rewrap_title": "Перешифрование Transit", transit_rewrap_title: "Перешифрование Transit",
"transit_rewrap_suffix": " (перешифр.)", transit_rewrap_suffix: " (перешифр.)",
"transit_rewrap_version_option_text": "{{version_num}}", transit_rewrap_version_option_text: "{{version_num}}",
"transit_rewrap_latest_version_option_text": "{{version_num}} (последняя версия)", transit_rewrap_latest_version_option_text: "{{version_num}} (последняя версия)",
"transit_rewrap_input_placeholder": "Шифр-текст", transit_rewrap_input_placeholder: "Шифр-текст",
"transit_rewrap_rewrap_btn": "Перешифровать", transit_rewrap_rewrap_btn: "Перешифровать",
"transit_rewrap_result_modal_title": "Результат перешифрования", transit_rewrap_result_modal_title: "Результат перешифрования",
} };

View file

@ -1,4 +1,4 @@
// These are all errors that the user won't see // These are all errors that the user won't see
// but are used internally. // but are used internally.
export const DoesNotExistError: Error = new Error("Does not exist.") export const DoesNotExistError: Error = new Error("Does not exist.");

View file

@ -2,17 +2,19 @@ export function removeDoubleSlash(str: string): string {
return str.replace(/\/\/+/g, "/"); return str.replace(/\/\/+/g, "/");
} }
export const getObjectKeys = export const getObjectKeys = (obj: Record<string, unknown>): string[] =>
(obj: Record<string, unknown>): string[] => Object.getOwnPropertyNames(obj); Object.getOwnPropertyNames(obj);
export const objectToMap = export const objectToMap = (obj: Record<string, unknown>): Map<string, unknown> =>
(obj: Record<string, unknown>): Map<string, unknown> => new Map(Object.entries(obj)); new Map(Object.entries(obj));
export const sortedObjectMap = export const sortedObjectMap = (obj: Record<string, unknown>): Map<string, unknown> =>
(obj: Record<string, unknown>): Map<string, unknown> => new Map(Object.entries(obj).sort()); 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)) { for (const key of getObjectKeys(map)) {
if (map[key] === searchValue) if (map[key] === searchValue) return key;
return key;
} }
} }