w-funnel/src/lib/funnel/storage.ts
dev.daminik00 c4ba2ff541 fix
2025-10-06 03:26:20 +02:00

158 lines
3.8 KiB
TypeScript

import type { FunnelAnswers } from "./types";
/**
* Структура данных для хранения состояния воронки
*/
export interface FunnelRuntimeState {
answers: FunnelAnswers;
history: string[];
version: number;
timestamp?: number; // Для отслеживания времени последнего обновления
}
/**
* Префикс для ключей в localStorage
*/
const STORAGE_PREFIX = "funnel_state_";
/**
* Время жизни данных в localStorage (7 дней)
*/
const STORAGE_TTL = 7 * 24 * 60 * 60 * 1000;
/**
* Проверяет доступность localStorage
*/
function isLocalStorageAvailable(): boolean {
try {
const testKey = "__storage_test__";
localStorage.setItem(testKey, "test");
localStorage.removeItem(testKey);
return true;
} catch {
return false;
}
}
/**
* Получает ключ для localStorage по ID воронки
*/
function getStorageKey(funnelId: string): string {
return `${STORAGE_PREFIX}${funnelId}`;
}
/**
* Сохраняет состояние воронки в localStorage
*/
export function saveFunnelState(
funnelId: string,
state: FunnelRuntimeState
): void {
if (!isLocalStorageAvailable()) {
return;
}
try {
const dataToSave: FunnelRuntimeState = {
...state,
timestamp: Date.now(),
};
localStorage.setItem(getStorageKey(funnelId), JSON.stringify(dataToSave));
} catch (error) {
console.warn("Failed to save funnel state to localStorage:", error);
}
}
/**
* Загружает состояние воронки из localStorage
*/
export function loadFunnelState(funnelId: string): FunnelRuntimeState | null {
if (!isLocalStorageAvailable()) {
return null;
}
try {
const stored = localStorage.getItem(getStorageKey(funnelId));
if (!stored) {
return null;
}
const data = JSON.parse(stored) as FunnelRuntimeState;
// Проверяем TTL
if (data.timestamp) {
const age = Date.now() - data.timestamp;
if (age > STORAGE_TTL) {
// Данные устарели, удаляем
clearFunnelState(funnelId);
return null;
}
}
return data;
} catch (error) {
console.warn("Failed to load funnel state from localStorage:", error);
return null;
}
}
/**
* Очищает состояние воронки из localStorage
*/
export function clearFunnelState(funnelId: string): void {
if (!isLocalStorageAvailable()) {
return;
}
try {
localStorage.removeItem(getStorageKey(funnelId));
} catch (error) {
console.warn("Failed to clear funnel state from localStorage:", error);
}
}
/**
* Очищает все устаревшие состояния воронок
*/
export function clearExpiredFunnelStates(): void {
if (!isLocalStorageAvailable()) {
return;
}
try {
const now = Date.now();
const keysToRemove: string[] = [];
// Проходим по всем ключам в localStorage
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (!key?.startsWith(STORAGE_PREFIX)) {
continue;
}
const value = localStorage.getItem(key);
if (!value) {
continue;
}
try {
const data = JSON.parse(value) as FunnelRuntimeState;
if (data.timestamp && now - data.timestamp > STORAGE_TTL) {
keysToRemove.push(key);
}
} catch {
// Если не удалось распарсить - удаляем
keysToRemove.push(key);
}
}
// Удаляем устаревшие ключи
for (const key of keysToRemove) {
localStorage.removeItem(key);
}
} catch (error) {
console.warn("Failed to clear expired funnel states:", error);
}
}