// 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("#secretRevealModal"); } function clearSecretValue(): void { const input = document.querySelector("#secretRevealValue"); const absent = document.querySelector("#secretRevealValueAbsent"); const result = document.querySelector("#secretRevealResultPanel"); const confirmation = document.querySelector("#secretRevealConfirmPanel"); const confirm = document.querySelector("#secretRevealConfirmAction"); const toggle = document.querySelector("#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("#secretRevealValue"); const absent = document.querySelector("#secretRevealValueAbsent"); const result = document.querySelector("#secretRevealResultPanel"); const confirmation = document.querySelector("#secretRevealConfirmPanel"); const confirm = document.querySelector("#secretRevealConfirmAction"); const toggle = document.querySelector("#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 { if (!pendingRequest || revealInFlight) { return; } const request = pendingRequest; const requestGeneration = revealGeneration; const confirm = document.querySelector("#secretRevealConfirmAction"); const status = document.querySelector("#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("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("#secretRevealValue"); const toggle = document.querySelector("#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("#secretRevealVariable"); const sourceElement = document.querySelector("#secretRevealSource"); const status = document.querySelector("#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("#secretRevealConfirmAction")?.addEventListener("click", () => { void executeReveal(); }); document.querySelector("#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"); }