Files
mkd/services/offlineCacheService.ts

142 lines
4.0 KiB
TypeScript
Raw Permalink Normal View History

2026-02-04 00:17:04 +05:00
// Сервис кэширования данных для offline режима
type CachedRequest = {
id: string;
method: string;
url: string;
body?: unknown;
timestamp: number;
retries: number;
};
const CACHE_KEY = 'mkd_offline_cache';
const MAX_RETRIES = 10;
const MAX_CACHE_AGE = 7 * 24 * 60 * 60 * 1000; // 7 дней
class OfflineCacheService {
// Сохранить запрос в кэш
cacheRequest(method: string, url: string, body?: unknown): string {
const cachedRequests = this.getCachedRequests();
const id = `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const request: CachedRequest = {
id,
method,
url,
body,
timestamp: Date.now(),
retries: 0,
};
cachedRequests.push(request);
this.saveCachedRequests(cachedRequests);
return id;
}
// Получить все закэшированные запросы
getCachedRequests(): CachedRequest[] {
try {
const stored = localStorage.getItem(CACHE_KEY);
if (stored) {
const requests = JSON.parse(stored) as CachedRequest[];
// Фильтруем устаревшие запросы
const now = Date.now();
return requests.filter(req => now - req.timestamp < MAX_CACHE_AGE);
}
return [];
} catch (e) {
console.error('Failed to get cached requests', e);
return [];
}
}
// Сохранить закэшированные запросы
private saveCachedRequests(requests: CachedRequest[]) {
try {
localStorage.setItem(CACHE_KEY, JSON.stringify(requests));
} catch (e) {
console.error('Failed to save cached requests', e);
}
}
// Удалить запрос из кэша
removeRequest(id: string): void {
const cachedRequests = this.getCachedRequests();
const filtered = cachedRequests.filter(req => req.id !== id);
this.saveCachedRequests(filtered);
}
// Увеличить счётчик попыток для запроса
incrementRetries(id: string): void {
const cachedRequests = this.getCachedRequests();
const updated = cachedRequests.map(req => {
if (req.id === id) {
return { ...req, retries: req.retries + 1 };
}
return req;
});
this.saveCachedRequests(updated);
}
// Синхронизировать все закэшированные запросы
async syncCachedRequests(apiBaseUrl: string): Promise<{ success: number; failed: number }> {
const cachedRequests = this.getCachedRequests();
let success = 0;
let failed = 0;
for (const request of cachedRequests) {
// Пропускаем запросы с превышенным лимитом попыток
if (request.retries >= MAX_RETRIES) {
this.removeRequest(request.id);
failed++;
continue;
}
try {
const fullUrl = request.url.startsWith('http')
? request.url
: `${apiBaseUrl}${request.url}`;
const options: RequestInit = {
method: request.method,
headers: {
'Content-Type': 'application/json',
},
};
if (request.body && (request.method === 'POST' || request.method === 'PUT')) {
options.body = JSON.stringify(request.body);
}
const response = await fetch(fullUrl, options);
if (response.ok) {
this.removeRequest(request.id);
success++;
} else {
this.incrementRetries(request.id);
failed++;
}
} catch (error) {
this.incrementRetries(request.id);
failed++;
}
}
return { success, failed };
}
// Очистить весь кэш
clearCache(): void {
localStorage.removeItem(CACHE_KEY);
}
// Получить количество закэшированных запросов
getCacheSize(): number {
return this.getCachedRequests().length;
}
}
// Создаём единственный экземпляр сервиса
export const offlineCacheService = new OfflineCacheService();