-
Notifications
You must be signed in to change notification settings - Fork 162
Expand file tree
/
Copy pathOfflineSyncManager.ts
More file actions
157 lines (135 loc) · 4.14 KB
/
OfflineSyncManager.ts
File metadata and controls
157 lines (135 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* OfflineSyncManager
*
* Handles offline capabilities in a Microservices Architecture.
* Queues requests when offline and intelligently routes them to
* the appropriate microservice (Auth, Groups, Courses, etc.)
* once the connection is restored.
*/
export type MicroserviceTarget = 'auth' | 'users' | 'courses' | 'groups' | 'certificates';
export interface OfflineRequest {
id: string;
targetService: MicroserviceTarget;
endpoint: string;
method: 'POST' | 'PUT' | 'PATCH' | 'DELETE';
headers?: Record<string, string>;
body: any;
timestamp: number;
}
export interface SyncConfig {
apiGatewayUrl?: string;
serviceUrls?: Record<MicroserviceTarget, string>;
}
const STORAGE_KEY = 'teachlink_offline_queue_v1';
export class OfflineSyncManager {
private queue: OfflineRequest[] = [];
private isOnline: boolean = true;
private config: SyncConfig;
private isSyncing: boolean = false;
constructor(config: SyncConfig = {}) {
this.config = config;
if (typeof window !== 'undefined') {
this.isOnline = navigator.onLine;
this.loadQueue();
this.setupListeners();
}
}
/**
* Initialize event listeners for network changes
*/
private setupListeners(): void {
window.addEventListener('online', this.handleOnline.bind(this));
window.addEventListener('offline', this.handleOffline.bind(this));
}
private handleOnline(): void {
this.isOnline = true;
this.processQueue();
}
private handleOffline(): void {
this.isOnline = false;
}
/**
* Load the persisted queue from localStorage
*/
private loadQueue(): void {
try {
const data = localStorage.getItem(STORAGE_KEY);
if (data) {
this.queue = JSON.parse(data);
}
} catch (error) {
console.error('Failed to load offline queue:', error);
this.queue = [];
}
}
/**
* Persist the queue to localStorage
*/
private saveQueue(): void {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.queue));
} catch (error) {
console.error('Failed to save offline queue:', error);
}
}
/**
* Enqueue a request to a specific microservice to be processed when online
*/
public enqueueRequest(request: Omit<OfflineRequest, 'id' | 'timestamp'>): string {
const id = `req_${Math.random().toString(36).substring(2, 9)}_${Date.now()}`;
const fullRequest: OfflineRequest = {
...request,
id,
timestamp: Date.now(),
};
this.queue.push(fullRequest);
this.saveQueue();
// Attempt to process immediately if online
if (this.isOnline) {
this.processQueue();
}
return id;
}
/**
* Process all queued requests, routing them to the correct microservice
*/
public async processQueue(): Promise<void> {
if (!this.isOnline || this.isSyncing || this.queue.length === 0) return;
this.isSyncing = true;
// Sort queue chronologically
this.queue.sort((a, b) => a.timestamp - b.timestamp);
const queueSnapshot = [...this.queue];
for (const request of queueSnapshot) {
try {
const baseUrl =
this.config.serviceUrls?.[request.targetService] || this.config.apiGatewayUrl || '';
const url = `${baseUrl}${request.endpoint}`;
const response = await fetch(url, {
method: request.method,
headers: {
'Content-Type': 'application/json',
...request.headers,
},
body: JSON.stringify(request.body),
});
if (response.ok) {
// Remove successful request from queue
this.queue = this.queue.filter((r) => r.id !== request.id);
this.saveQueue();
} else {
// Stop processing if we hit a server error to maintain chronological order
console.warn(
`Failed to sync request ${request.id} to ${request.targetService}. Status: ${response.status}`,
);
break;
}
} catch (error) {
console.warn(
`Network error while syncing request ${request.id} to ${request.targetService}. Will retry later.`,
);
break; // Stop processing on network error
}
}
this.isSyncing = false;
}
}