Files
trackgo-fe/packages/core-storage/src/local-storage.service.ts
T

92 lines
2.7 KiB
TypeScript

import { EncryptionUtils } from '@repo/utils';
import type { IStorageService } from './storage.interface';
export interface StorageOptions<TKey extends string> {
encryptedKeys?: Set<TKey>;
plainTextKeys?: Set<TKey>;
}
/**
* Enterprise-grade localStorage wrapper with optional AES encryption.
*/
export class LocalStorageService<TKey extends string> implements IStorageService<TKey> {
private readonly encryption: EncryptionUtils;
private readonly encryptedKeys: Set<TKey>;
private readonly plainTextKeys: Set<TKey>;
constructor(options?: StorageOptions<TKey>, encryptionUtils?: EncryptionUtils) {
this.encryption = encryptionUtils ?? EncryptionUtils.getInstance();
this.encryptedKeys = options?.encryptedKeys ?? new Set();
this.plainTextKeys = options?.plainTextKeys ?? new Set();
}
private validateKey(key: TKey): void {
if (!this.encryptedKeys.has(key) && !this.plainTextKeys.has(key)) {
throw new Error(`[Storage Engine] Security Exception: Key '${key}' is not registered and cannot be accessed.`);
}
}
private shouldEncrypt(key: TKey): boolean {
return this.encryptedKeys.has(key);
}
async setItem<T>(key: TKey, value: T): Promise<void> {
this.validateKey(key);
const serialized = JSON.stringify(value);
if (this.shouldEncrypt(key)) {
const encrypted = this.encryption.encrypt(serialized);
localStorage.setItem(key as string, encrypted);
} else {
localStorage.setItem(key as string, serialized);
}
}
async getItem<T>(key: TKey): Promise<T | null> {
this.validateKey(key);
const raw = localStorage.getItem(key as string);
if (raw === null) return null;
try {
if (this.shouldEncrypt(key)) {
const decrypted = this.encryption.decrypt(raw);
if (!decrypted) return null;
return JSON.parse(decrypted) as T;
}
return JSON.parse(raw) as T;
} catch {
console.warn(`[core-storage] Failed to parse key "${key}". Removing corrupt entry.`);
localStorage.removeItem(key as string);
return null;
}
}
async removeItem(key: TKey): Promise<void> {
this.validateKey(key);
localStorage.removeItem(key as string);
}
async clear(): Promise<void> {
localStorage.clear();
}
async hasItem(key: TKey): Promise<boolean> {
return localStorage.getItem(key as string) !== null;
}
async keys(): Promise<TKey[]> {
const result: TKey[] = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key !== null) result.push(key as TKey);
}
return result;
}
}
export function createLocalStorage<TKey extends string>(
options?: StorageOptions<TKey>
): IStorageService<TKey> {
return new LocalStorageService<TKey>(options);
}