Files
khadhroony-solana-project/crates/ksp-app-config-desk/frontend/ts/secret_reveal.ts
2026-08-16 16:51:11 +02:00

207 lines
7.8 KiB
TypeScript

// file: crates/ksp-app-config-desk/frontend/ts/secret_reveal.ts
// version: 1
import { Modal } from "bootstrap";
import type { SecretRevealRequestDto } from "./bindings/ksp_app_config_desk/secrets/SecretRevealRequestDto";
import type { SecretRevealResponseDto } from "./bindings/ksp_app_config_desk/secrets/SecretRevealResponseDto";
import { frontendDebug, frontendTrace } from "./frontend_log";
import { invokeKsp } from "./invoke";
export type SecretRevealSource = "effective" | "dotenv";
let pendingRequest: SecretRevealRequestDto | null = null;
let revealInFlight = false;
let revealGeneration = 0;
function sourceLabel(source: SecretRevealSource): string {
return source === "effective" ? "effective (process prioritaire sur .env)" : ".env persisté";
}
function modalElement(): HTMLElement | null {
return document.querySelector<HTMLElement>("#secretRevealModal");
}
function clearSecretValue(): void {
const input = document.querySelector<HTMLInputElement>("#secretRevealValue");
const absent = document.querySelector<HTMLElement>("#secretRevealValueAbsent");
const result = document.querySelector<HTMLElement>("#secretRevealResultPanel");
const confirmation = document.querySelector<HTMLElement>("#secretRevealConfirmPanel");
const confirm = document.querySelector<HTMLButtonElement>("#secretRevealConfirmAction");
const toggle = document.querySelector<HTMLButtonElement>("#secretRevealToggleVisibility");
if (input) {
input.value = "";
input.type = "password";
}
if (absent) {
absent.hidden = true;
}
if (result) {
result.hidden = true;
}
if (confirmation) {
confirmation.hidden = false;
}
if (confirm) {
confirm.hidden = false;
confirm.disabled = false;
}
if (toggle) {
toggle.hidden = true;
toggle.textContent = "Afficher";
}
}
function renderRevealResponse(response: SecretRevealResponseDto): void {
const input = document.querySelector<HTMLInputElement>("#secretRevealValue");
const absent = document.querySelector<HTMLElement>("#secretRevealValueAbsent");
const result = document.querySelector<HTMLElement>("#secretRevealResultPanel");
const confirmation = document.querySelector<HTMLElement>("#secretRevealConfirmPanel");
const confirm = document.querySelector<HTMLButtonElement>("#secretRevealConfirmAction");
const toggle = document.querySelector<HTMLButtonElement>("#secretRevealToggleVisibility");
if (confirmation) {
confirmation.hidden = true;
}
if (confirm) {
confirm.hidden = true;
}
if (result) {
result.hidden = false;
}
if (response.value === null) {
if (input) {
input.value = "";
input.hidden = true;
}
if (absent) {
absent.hidden = false;
}
if (toggle) {
toggle.hidden = true;
}
} else {
if (input) {
input.hidden = false;
input.type = "password";
input.value = response.value;
}
if (absent) {
absent.hidden = true;
}
if (toggle) {
toggle.hidden = false;
toggle.textContent = "Afficher";
}
}
frontendDebug("main", "Privileged Secret reveal rendered", {
variableName: response.variableName,
source: response.source,
valuePresent: response.value !== null,
});
}
async function executeReveal(): Promise<void> {
if (!pendingRequest || revealInFlight) {
return;
}
const request = pendingRequest;
const requestGeneration = revealGeneration;
const confirm = document.querySelector<HTMLButtonElement>("#secretRevealConfirmAction");
const status = document.querySelector<HTMLElement>("#secretRevealStatus");
revealInFlight = true;
if (confirm) {
confirm.disabled = true;
}
if (status) {
status.textContent = "Reveal privilégié en cours via ksp-config-lib...";
}
frontendDebug("main", "Privileged Secret reveal requested", { variableName: request.variableName, source: request.source });
try {
const response = await invokeKsp<SecretRevealResponseDto>("main", "reveal_environment_value", { request });
if (requestGeneration !== revealGeneration || !pendingRequest) {
frontendDebug("main", "Privileged Secret reveal response discarded after modal state changed", {
variableName: request.variableName,
source: request.source,
});
return;
}
renderRevealResponse(response);
if (status) {
status.textContent = response.value === null ? "Aucune valeur n'existe dans la source demandée." : "Valeur révélée temporairement. Elle sera effacée à la fermeture.";
}
} catch {
if (status) {
status.textContent = "Le reveal a été refusé ou a échoué.";
}
if (confirm) {
confirm.disabled = false;
}
} finally {
if (requestGeneration === revealGeneration) {
revealInFlight = false;
}
}
}
function toggleRevealVisibility(): void {
const input = document.querySelector<HTMLInputElement>("#secretRevealValue");
const toggle = document.querySelector<HTMLButtonElement>("#secretRevealToggleVisibility");
if (!input || !toggle || input.hidden) {
return;
}
const show = input.type === "password";
input.type = show ? "text" : "password";
toggle.textContent = show ? "Masquer" : "Afficher";
frontendDebug("main", "Transient Secret reveal visibility changed", { visible: show });
}
export function requestSecretReveal(variableName: string, source: SecretRevealSource): void {
const modal = modalElement();
const variable = document.querySelector<HTMLElement>("#secretRevealVariable");
const sourceElement = document.querySelector<HTMLElement>("#secretRevealSource");
const status = document.querySelector<HTMLElement>("#secretRevealStatus");
if (!modal || !variable || !sourceElement || !status) {
frontendDebug("main", "Privileged Secret reveal modal is unavailable", { variableName, source });
return;
}
revealGeneration += 1;
clearSecretValue();
pendingRequest = { variableName, source };
variable.textContent = variableName;
sourceElement.textContent = sourceLabel(source);
status.textContent = "Confirmation explicite requise avant tout accès à la valeur réelle.";
frontendDebug("main", "Privileged Secret reveal confirmation opened", { variableName, source });
Modal.getOrCreateInstance(modal).show();
}
export function clearTransientSecretReveal(): void {
const modal = modalElement();
const variableName = pendingRequest?.variableName ?? null;
revealGeneration += 1;
clearSecretValue();
pendingRequest = null;
revealInFlight = false;
if (modal) {
Modal.getOrCreateInstance(modal).hide();
}
frontendTrace("main", "Transient Secret reveal state cleared", { variableName });
}
export function initializeSecretReveal(): void {
const modal = modalElement();
document.querySelector<HTMLButtonElement>("#secretRevealConfirmAction")?.addEventListener("click", () => {
void executeReveal();
});
document.querySelector<HTMLButtonElement>("#secretRevealToggleVisibility")?.addEventListener("click", () => {
toggleRevealVisibility();
});
modal?.addEventListener("hidden.bs.modal", () => {
const variableName = pendingRequest?.variableName ?? null;
revealGeneration += 1;
clearSecretValue();
pendingRequest = null;
revealInFlight = false;
frontendDebug("main", "Privileged Secret reveal closed and value cleared", { variableName });
});
frontendTrace("main", "Privileged Secret reveal handlers installed");
}