initial code commit
This commit is contained in:
commit
27bb45f7df
56 changed files with 15106 additions and 0 deletions
79
client/src/lib/storage/vault.ts
Normal file
79
client/src/lib/storage/vault.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* Vault — the bridge between app state, encryption, and storage adapters.
|
||||
*/
|
||||
|
||||
import type { AppData, StorageMode } from '@/types';
|
||||
import { encrypt, decrypt } from '@/lib/crypto/encryption';
|
||||
import {
|
||||
CookieStorage,
|
||||
FileStorage,
|
||||
CloudStorage,
|
||||
type StorageAdapter,
|
||||
} from './adapters';
|
||||
|
||||
export interface VaultConfig {
|
||||
mode: StorageMode;
|
||||
username: string;
|
||||
password: string;
|
||||
/** Required iff mode === 'cloud' */
|
||||
apiUrl?: string;
|
||||
getCloudToken?: () => string | null;
|
||||
}
|
||||
|
||||
export class Vault {
|
||||
private adapter: StorageAdapter;
|
||||
private password: string;
|
||||
/** File adapter is held separately so the UI can call setFile() */
|
||||
public readonly fileAdapter: FileStorage;
|
||||
|
||||
constructor(cfg: VaultConfig) {
|
||||
this.password = cfg.password;
|
||||
this.fileAdapter = new FileStorage();
|
||||
switch (cfg.mode) {
|
||||
case 'file':
|
||||
this.adapter = this.fileAdapter;
|
||||
break;
|
||||
case 'cloud':
|
||||
if (!cfg.apiUrl || !cfg.getCloudToken) {
|
||||
throw new Error('Cloud storage requires apiUrl and getCloudToken');
|
||||
}
|
||||
this.adapter = new CloudStorage(cfg.apiUrl, cfg.getCloudToken);
|
||||
break;
|
||||
case 'cookie':
|
||||
default:
|
||||
this.adapter = new CookieStorage(cfg.username);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async save(data: AppData): Promise<void> {
|
||||
const json = JSON.stringify(data);
|
||||
const blob = await encrypt(json, this.password);
|
||||
await this.adapter.save(blob);
|
||||
}
|
||||
|
||||
async load(): Promise<AppData | null> {
|
||||
const blob = await this.adapter.load();
|
||||
if (!blob) return null;
|
||||
const json = await decrypt(blob, this.password);
|
||||
return JSON.parse(json);
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
await this.adapter.clear();
|
||||
}
|
||||
|
||||
/** Export current data as an encrypted file regardless of active storage mode */
|
||||
async exportToFile(data: AppData): Promise<void> {
|
||||
const json = JSON.stringify(data);
|
||||
const blob = await encrypt(json, this.password);
|
||||
await this.fileAdapter.save(blob);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a user already has encrypted data under their username (cookie mode) */
|
||||
export async function cookieDataExists(username: string): Promise<boolean> {
|
||||
const cs = new CookieStorage(username);
|
||||
const blob = await cs.load();
|
||||
return blob != null;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue