atagen

malice

public
Star0
Watch0
5dc8b4770e1cb5fafe24d0b08076a46bda0d9605
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
web/src/lib/protection-model.tsTypescript64 lines2.0 KB
1
import type { EncryptionState } from './types';
2
3
export type RecoveryPrompt = 'recover' | 'setup';
4
5
export interface InstallContext {
6
  device: boolean;
7
  standalone: boolean;
8
}
9
10
export interface PersistResult {
11
  persisted: boolean;
12
  usage: number | null;
13
  quota: number | null;
14
}
15
16
export interface StorageLike {
17
  getItem(key: string): string | null;
18
  setItem(key: string, value: string): void;
19
}
20
21
export interface PersistManager {
22
  persisted?(): Promise<boolean>;
23
  persist?(): Promise<boolean>;
24
  estimate?(): Promise<{ usage?: number; quota?: number }>;
25
}
26
27
export function canDismissRecovery(ctx: InstallContext): boolean {
28
  return ctx.device && !ctx.standalone;
29
}
30
31
export function recoveryPrompt(state: EncryptionState | null, ctx: InstallContext, dismissed: boolean): RecoveryPrompt | null {
32
  if (!state) return null;
33
  if (state.recovery === 'Incomplete') return 'recover';
34
  if (state.recovery === 'Disabled') return dismissed && canDismissRecovery(ctx) ? null : 'setup';
35
  return null;
36
}
37
38
export function dismissalKey(kind: 'recovery' | 'storage', account: string): string {
39
  return `malice.dismissed.${kind}.${account}`;
40
}
41
42
export function isDismissed(store: StorageLike, kind: 'recovery' | 'storage', account: string): boolean {
43
  try {
44
    return store.getItem(dismissalKey(kind, account)) === '1';
45
  } catch {
46
    return false;
47
  }
48
}
49
50
export function setDismissed(store: StorageLike, kind: 'recovery' | 'storage', account: string): void {
51
  try {
52
    store.setItem(dismissalKey(kind, account), '1');
53
  } catch {
54
    // dismissal then lasts only for this page load
55
  }
56
}
57
58
export async function requestPersistence(manager: PersistManager | undefined): Promise<PersistResult | null> {
59
  if (!manager?.persist || !manager.persisted) return null;
60
  let persisted = await manager.persisted();
61
  if (!persisted) persisted = await manager.persist();
62
  const estimate = manager.estimate ? await manager.estimate().catch(() => null) : null;
63
  return { persisted, usage: estimate?.usage ?? null, quota: estimate?.quota ?? null };
64
}