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