feat(ui): encryption-vault panel in Settings privacy (enable/unlock/lock/change) (T8.8)
This commit is contained in:
@@ -63,6 +63,58 @@
|
||||
let showConsent = $state(false);
|
||||
let testResult = $state<{ ok: boolean; message: string } | null>(null);
|
||||
|
||||
// ---- At-rest encryption vault (T8.8, FR-SEC-3) ----
|
||||
let vault = $state<{ enabled: boolean; unlocked: boolean } | null>(null);
|
||||
let vaultPw = $state("");
|
||||
let vaultPw2 = $state("");
|
||||
let vaultMsg = $state<string | null>(null);
|
||||
async function loadVault() {
|
||||
try {
|
||||
vault = await api.vaultStatus();
|
||||
} catch {
|
||||
vault = null;
|
||||
}
|
||||
}
|
||||
onMount(loadVault);
|
||||
async function enableVault() {
|
||||
vaultMsg = null;
|
||||
try {
|
||||
await api.enableVault(vaultPw);
|
||||
vaultPw = "";
|
||||
vaultMsg = "Vault enabled and unlocked.";
|
||||
await loadVault();
|
||||
} catch (e) {
|
||||
vaultMsg = `${e}`;
|
||||
}
|
||||
}
|
||||
async function unlockVault() {
|
||||
vaultMsg = null;
|
||||
try {
|
||||
await api.unlockVault(vaultPw);
|
||||
vaultPw = "";
|
||||
vaultMsg = "Unlocked.";
|
||||
await loadVault();
|
||||
} catch (e) {
|
||||
vaultMsg = `${e}`;
|
||||
}
|
||||
}
|
||||
async function lockVault() {
|
||||
await api.lockVault();
|
||||
vaultMsg = "Locked.";
|
||||
await loadVault();
|
||||
}
|
||||
async function changeVaultPassword() {
|
||||
vaultMsg = null;
|
||||
try {
|
||||
await api.changeVaultPassword(vaultPw, vaultPw2);
|
||||
vaultPw = "";
|
||||
vaultPw2 = "";
|
||||
vaultMsg = "Password changed.";
|
||||
} catch (e) {
|
||||
vaultMsg = `${e}`;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- NPU package download (T3.4) ----
|
||||
let npuBusy = $state(false);
|
||||
let npuMsg = $state<string | null>(null);
|
||||
@@ -612,6 +664,56 @@
|
||||
{:else}
|
||||
<p class="muted">Privacy self-check unavailable.</p>
|
||||
{/if}
|
||||
|
||||
{#if vault}
|
||||
<h4>Encryption vault</h4>
|
||||
{#if !vault.enabled}
|
||||
<p class="muted">
|
||||
Encrypt notes, transcripts, and summaries at rest with a password. (Audio files are
|
||||
not encrypted yet.)
|
||||
</p>
|
||||
<div class="grid">
|
||||
<label class="wide"
|
||||
>Vault password<input type="password" bind:value={vaultPw} /></label
|
||||
>
|
||||
</div>
|
||||
<button class="primary" onclick={enableVault} disabled={vaultPw.length < 8}
|
||||
>Enable vault</button
|
||||
>
|
||||
<p class="muted">
|
||||
Use at least 8 characters. If you forget it, encrypted content can't be recovered.
|
||||
</p>
|
||||
{:else if !vault.unlocked}
|
||||
<p class="muted">
|
||||
Vault is <strong>locked</strong>. Unlock to read encrypted meetings.
|
||||
</p>
|
||||
<div class="grid">
|
||||
<label class="wide">Password<input type="password" bind:value={vaultPw} /></label>
|
||||
</div>
|
||||
<button class="primary" onclick={unlockVault} disabled={!vaultPw}>Unlock</button>
|
||||
{:else}
|
||||
<p class="muted">
|
||||
Vault is <strong>unlocked</strong>. New notes, transcripts, and summaries are
|
||||
encrypted at rest.
|
||||
</p>
|
||||
<button onclick={lockVault}>Lock now</button>
|
||||
<details>
|
||||
<summary>Change password</summary>
|
||||
<div class="grid">
|
||||
<label class="wide"
|
||||
>Current password<input type="password" bind:value={vaultPw} /></label
|
||||
>
|
||||
<label class="wide"
|
||||
>New password<input type="password" bind:value={vaultPw2} /></label
|
||||
>
|
||||
</div>
|
||||
<button onclick={changeVaultPassword} disabled={!vaultPw || vaultPw2.length < 8}
|
||||
>Change password</button
|
||||
>
|
||||
</details>
|
||||
{/if}
|
||||
{#if vaultMsg}<p class="muted">{vaultMsg}</p>{/if}
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user