Some ignored filles was missing
This commit is contained in:
parent
7db22245e2
commit
fd5ab6bc0d
268 changed files with 31970 additions and 4 deletions
229
wwwroot/js/workers/SyncManager.js
Normal file
229
wwwroot/js/workers/SyncManager.js
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
import { CoreEvents } from '../constants/CoreEvents';
|
||||
/**
|
||||
* SyncManager - Background sync worker
|
||||
* Processes operation queue and syncs with API when online
|
||||
*
|
||||
* Features:
|
||||
* - Monitors online/offline status
|
||||
* - Processes queue with FIFO order
|
||||
* - Exponential backoff retry logic
|
||||
* - Updates syncStatus in IndexedDB after successful sync
|
||||
* - Emits sync events for UI feedback
|
||||
*/
|
||||
export class SyncManager {
|
||||
constructor(eventBus, queue, indexedDB, apiRepository) {
|
||||
this.isOnline = navigator.onLine;
|
||||
this.isSyncing = false;
|
||||
this.syncInterval = 5000; // 5 seconds
|
||||
this.maxRetries = 5;
|
||||
this.intervalId = null;
|
||||
this.eventBus = eventBus;
|
||||
this.queue = queue;
|
||||
this.indexedDB = indexedDB;
|
||||
this.apiRepository = apiRepository;
|
||||
this.setupNetworkListeners();
|
||||
this.startSync();
|
||||
console.log('SyncManager initialized and started');
|
||||
}
|
||||
/**
|
||||
* Setup online/offline event listeners
|
||||
*/
|
||||
setupNetworkListeners() {
|
||||
window.addEventListener('online', () => {
|
||||
this.isOnline = true;
|
||||
this.eventBus.emit(CoreEvents.OFFLINE_MODE_CHANGED, {
|
||||
isOnline: true
|
||||
});
|
||||
console.log('SyncManager: Network online - starting sync');
|
||||
this.startSync();
|
||||
});
|
||||
window.addEventListener('offline', () => {
|
||||
this.isOnline = false;
|
||||
this.eventBus.emit(CoreEvents.OFFLINE_MODE_CHANGED, {
|
||||
isOnline: false
|
||||
});
|
||||
console.log('SyncManager: Network offline - pausing sync');
|
||||
this.stopSync();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Start background sync worker
|
||||
*/
|
||||
startSync() {
|
||||
if (this.intervalId) {
|
||||
return; // Already running
|
||||
}
|
||||
console.log('SyncManager: Starting background sync');
|
||||
// Process immediately
|
||||
this.processQueue();
|
||||
// Then poll every syncInterval
|
||||
this.intervalId = window.setInterval(() => {
|
||||
this.processQueue();
|
||||
}, this.syncInterval);
|
||||
}
|
||||
/**
|
||||
* Stop background sync worker
|
||||
*/
|
||||
stopSync() {
|
||||
if (this.intervalId) {
|
||||
window.clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
console.log('SyncManager: Stopped background sync');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Process operation queue
|
||||
* Sends pending operations to API
|
||||
*/
|
||||
async processQueue() {
|
||||
// Don't sync if offline
|
||||
if (!this.isOnline) {
|
||||
return;
|
||||
}
|
||||
// Don't start new sync if already syncing
|
||||
if (this.isSyncing) {
|
||||
return;
|
||||
}
|
||||
// Check if queue is empty
|
||||
if (await this.queue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.isSyncing = true;
|
||||
try {
|
||||
const operations = await this.queue.getAll();
|
||||
this.eventBus.emit(CoreEvents.SYNC_STARTED, {
|
||||
operationCount: operations.length
|
||||
});
|
||||
// Process operations one by one (FIFO)
|
||||
for (const operation of operations) {
|
||||
await this.processOperation(operation);
|
||||
}
|
||||
this.eventBus.emit(CoreEvents.SYNC_COMPLETED, {
|
||||
operationCount: operations.length
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error('SyncManager: Queue processing error:', error);
|
||||
this.eventBus.emit(CoreEvents.SYNC_FAILED, {
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
}
|
||||
finally {
|
||||
this.isSyncing = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Process a single operation
|
||||
*/
|
||||
async processOperation(operation) {
|
||||
// Check if max retries exceeded
|
||||
if (operation.retryCount >= this.maxRetries) {
|
||||
console.error(`SyncManager: Max retries exceeded for operation ${operation.id}`, operation);
|
||||
await this.queue.remove(operation.id);
|
||||
await this.markEventAsError(operation.eventId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Send to API based on operation type
|
||||
switch (operation.type) {
|
||||
case 'create':
|
||||
await this.apiRepository.sendCreate(operation.data);
|
||||
break;
|
||||
case 'update':
|
||||
await this.apiRepository.sendUpdate(operation.eventId, operation.data);
|
||||
break;
|
||||
case 'delete':
|
||||
await this.apiRepository.sendDelete(operation.eventId);
|
||||
break;
|
||||
default:
|
||||
console.error(`SyncManager: Unknown operation type ${operation.type}`);
|
||||
await this.queue.remove(operation.id);
|
||||
return;
|
||||
}
|
||||
// Success - remove from queue and mark as synced
|
||||
await this.queue.remove(operation.id);
|
||||
await this.markEventAsSynced(operation.eventId);
|
||||
console.log(`SyncManager: Successfully synced operation ${operation.id}`);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`SyncManager: Failed to sync operation ${operation.id}:`, error);
|
||||
// Increment retry count
|
||||
await this.queue.incrementRetryCount(operation.id);
|
||||
// Calculate backoff delay
|
||||
const backoffDelay = this.calculateBackoff(operation.retryCount + 1);
|
||||
this.eventBus.emit(CoreEvents.SYNC_RETRY, {
|
||||
operationId: operation.id,
|
||||
retryCount: operation.retryCount + 1,
|
||||
nextRetryIn: backoffDelay
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Mark event as synced in IndexedDB
|
||||
*/
|
||||
async markEventAsSynced(eventId) {
|
||||
try {
|
||||
const event = await this.indexedDB.getEvent(eventId);
|
||||
if (event) {
|
||||
event.syncStatus = 'synced';
|
||||
await this.indexedDB.saveEvent(event);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`SyncManager: Failed to mark event ${eventId} as synced:`, error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Mark event as error in IndexedDB
|
||||
*/
|
||||
async markEventAsError(eventId) {
|
||||
try {
|
||||
const event = await this.indexedDB.getEvent(eventId);
|
||||
if (event) {
|
||||
event.syncStatus = 'error';
|
||||
await this.indexedDB.saveEvent(event);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`SyncManager: Failed to mark event ${eventId} as error:`, error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Calculate exponential backoff delay
|
||||
* @param retryCount Current retry count
|
||||
* @returns Delay in milliseconds
|
||||
*/
|
||||
calculateBackoff(retryCount) {
|
||||
// Exponential backoff: 2^retryCount * 1000ms
|
||||
// Retry 1: 2s, Retry 2: 4s, Retry 3: 8s, Retry 4: 16s, Retry 5: 32s
|
||||
const baseDelay = 1000;
|
||||
const exponentialDelay = Math.pow(2, retryCount) * baseDelay;
|
||||
const maxDelay = 60000; // Max 1 minute
|
||||
return Math.min(exponentialDelay, maxDelay);
|
||||
}
|
||||
/**
|
||||
* Manually trigger sync (for testing or manual sync button)
|
||||
*/
|
||||
async triggerManualSync() {
|
||||
console.log('SyncManager: Manual sync triggered');
|
||||
await this.processQueue();
|
||||
}
|
||||
/**
|
||||
* Get current sync status
|
||||
*/
|
||||
getSyncStatus() {
|
||||
return {
|
||||
isOnline: this.isOnline,
|
||||
isSyncing: this.isSyncing,
|
||||
isRunning: this.intervalId !== null
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Cleanup - stop sync and remove listeners
|
||||
*/
|
||||
destroy() {
|
||||
this.stopSync();
|
||||
// Note: We don't remove window event listeners as they're global
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=SyncManager.js.map
|
||||
Loading…
Add table
Add a link
Reference in a new issue