195 lines
5.3 KiB
TypeScript
195 lines
5.3 KiB
TypeScript
import { MarginInline } from "../elements/MarginInline";
|
|
import { Page } from "../types/Page";
|
|
import { QRScanner, QRScannerType } from "../elements/QRScanner";
|
|
import { SealStatusType, getSealStatus } from "../api/sys/getSealStatus";
|
|
import { changePage, setErrorText, setPageContent } from "../pageUtils";
|
|
import { makeElement } from "../htmlUtils";
|
|
import { submitUnsealKey } from "../api/sys/submitUnsealKey";
|
|
import i18next from "i18next";
|
|
|
|
const UnsealInputModes = {
|
|
FORM_INPUT: "FORM_INPUT",
|
|
QR_INPUT: "QR_INPUT",
|
|
};
|
|
|
|
export class UnsealPage extends Page {
|
|
constructor() {
|
|
super();
|
|
this.mode = UnsealInputModes.FORM_INPUT;
|
|
}
|
|
|
|
mode: string;
|
|
refresher: number;
|
|
qrScanner: QRScannerType;
|
|
unsealProgress: HTMLProgressElement;
|
|
unsealProgressText: HTMLParagraphElement;
|
|
unsealInputContent: HTMLElement;
|
|
unsealKeyForm: HTMLFormElement;
|
|
|
|
async cleanup(): Promise<void> {
|
|
this.deinitWebcam();
|
|
clearInterval(this.refresher);
|
|
}
|
|
|
|
deinitWebcam(): void {
|
|
try {
|
|
this.qrScanner.deinit();
|
|
} catch (_) {
|
|
// Do Nothing
|
|
}
|
|
}
|
|
|
|
makeRefresher(): void {
|
|
const id = setInterval(() => {
|
|
void (this as UnsealPage).doRefresh().then(() => {});
|
|
return;
|
|
}, 1000);
|
|
this.refresher = id as unknown as number;
|
|
}
|
|
|
|
async doRefresh(): Promise<void> {
|
|
const status = await getSealStatus();
|
|
await this.updateSealProgress(status);
|
|
}
|
|
|
|
async render(): Promise<void> {
|
|
this.unsealProgress = makeElement({
|
|
tag: "progress",
|
|
class: "uk-progress",
|
|
attributes: { value: "0", max: "0" },
|
|
}) as HTMLProgressElement;
|
|
this.unsealProgressText = makeElement({
|
|
tag: "p",
|
|
text: i18next.t("unseal_keys_progress", {
|
|
progress: "0",
|
|
keys_needed: "0",
|
|
}),
|
|
}) as HTMLParagraphElement;
|
|
this.unsealInputContent = makeElement({
|
|
tag: "div",
|
|
});
|
|
setPageContent(
|
|
makeElement({
|
|
tag: "div",
|
|
children: [
|
|
this.unsealProgress,
|
|
makeElement({
|
|
tag: "p",
|
|
id: "errorText",
|
|
class: ["uk-text-danger", "uk-margin-top"],
|
|
}),
|
|
this.unsealProgressText,
|
|
this.unsealInputContent,
|
|
],
|
|
}),
|
|
);
|
|
this.switchInputMode(this.mode);
|
|
await this.updateSealProgress(await getSealStatus());
|
|
this.makeRefresher();
|
|
}
|
|
|
|
setButtons(method: string): void {
|
|
const newMethod: string =
|
|
method == UnsealInputModes.FORM_INPUT
|
|
? UnsealInputModes.QR_INPUT
|
|
: UnsealInputModes.FORM_INPUT;
|
|
const buttonText: string =
|
|
newMethod == UnsealInputModes.FORM_INPUT
|
|
? i18next.t("unseal_input_btn")
|
|
: i18next.t("unseal_qr_btn");
|
|
this.unsealInputContent.appendChild(
|
|
makeElement({
|
|
tag: "button",
|
|
class: ["uk-button", "uk-button-primary"],
|
|
text: buttonText,
|
|
onclick: () => {
|
|
this.switchInputMode(newMethod);
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
switchInputMode(method: string): void {
|
|
this.deinitWebcam();
|
|
this.unsealInputContent.querySelectorAll("*").forEach((n) => n.remove());
|
|
if (method == UnsealInputModes.FORM_INPUT) this.makeUnsealForm();
|
|
if (method == UnsealInputModes.QR_INPUT) void this.makeQRInput();
|
|
this.setButtons(method);
|
|
}
|
|
|
|
makeUnsealForm(): void {
|
|
this.unsealKeyForm = makeElement({
|
|
tag: "form",
|
|
children: [
|
|
MarginInline(
|
|
makeElement({
|
|
tag: "input",
|
|
class: ["uk-input", "uk-form-width-medium"],
|
|
attributes: {
|
|
required: "true",
|
|
type: "password",
|
|
placeholder: i18next.t("key_input_placeholder"),
|
|
name: "key",
|
|
},
|
|
}),
|
|
),
|
|
MarginInline(
|
|
makeElement({
|
|
tag: "button",
|
|
class: ["uk-button", "uk-button-primary"],
|
|
text: i18next.t("submit_key_btn"),
|
|
}),
|
|
),
|
|
],
|
|
}) as HTMLFormElement;
|
|
this.unsealInputContent.appendChild(this.unsealKeyForm);
|
|
this.unsealKeyForm.addEventListener("submit", (e: Event) => {
|
|
e.preventDefault();
|
|
void this.handleKeySubmit();
|
|
});
|
|
}
|
|
|
|
async makeQRInput(): Promise<void> {
|
|
this.qrScanner = await QRScanner(async (code: string) => {
|
|
await this.submitKey(code);
|
|
});
|
|
this.unsealInputContent.appendChild(this.qrScanner);
|
|
}
|
|
|
|
async updateSealProgress(data: SealStatusType): Promise<void> {
|
|
const progress = data.progress;
|
|
const keysNeeded = data.t;
|
|
const text = this.unsealProgressText;
|
|
text.innerText = i18next.t("unseal_keys_progress", {
|
|
progress: String(progress),
|
|
keys_needed: String(keysNeeded),
|
|
});
|
|
const progressBar = this.unsealProgress;
|
|
progressBar.value = progress;
|
|
progressBar.max = keysNeeded;
|
|
if (!data.sealed) {
|
|
progressBar.value = keysNeeded;
|
|
await changePage("HOME");
|
|
}
|
|
}
|
|
|
|
async submitKey(key: string): Promise<void> {
|
|
try {
|
|
await submitUnsealKey(key);
|
|
await this.updateSealProgress(await getSealStatus());
|
|
} catch (e: unknown) {
|
|
const error = e as Error;
|
|
setErrorText(error.message);
|
|
}
|
|
}
|
|
|
|
async handleKeySubmit(): Promise<void> {
|
|
const formData = new FormData(this.unsealKeyForm);
|
|
|
|
await this.submitKey(formData.get("key") as string);
|
|
}
|
|
get name(): string {
|
|
return i18next.t("unseal_vault_text");
|
|
}
|
|
}
|