142 lines
4.0 KiB
TypeScript
142 lines
4.0 KiB
TypeScript
|
|
// Сервис кэширования данных для 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();
|