Skip to content

Commit 03a02c3

Browse files
committed
sync rate limit
1 parent 45e927f commit 03a02c3

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

src/services/apiService.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,30 @@
1818
}
1919

2020
static async healthCheck(serverUrl: string): Promise<boolean> {
21+
// Check if we're currently rate limited
22+
const rateLimitUntil = localStorage.getItem('rate_limit_until');
23+
if (rateLimitUntil && Date.now() < parseInt(rateLimitUntil, 10)) {
24+
console.log('Skipping health check due to active rate limit');
25+
throw new Error('Rate limited, please try again later');
26+
}
27+
2128
try {
2229
const endpoint = this.getEndpoint(serverUrl, '/health');
2330
const response = await fetch(endpoint, {
31+
method: 'GET',
2432
headers: {
2533
'Accept': 'application/json',
2634
},
2735
});
2836

29-
if (!response.ok) {
30-
throw new Error('Health check failed');
37+
if (response.status === 429) {
38+
const retryAfter = response.headers.get('Retry-After');
39+
const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1000 : 30000;
40+
localStorage.setItem('rate_limit_until', (Date.now() + waitTime).toString());
41+
return false;
3142
}
3243

33-
const data = await response.json();
34-
return data.status === 'healthy' && data.database === 'connected';
44+
return response.ok;
3545
} catch (error) {
3646
console.error('Health check failed:', error);
3747
return false;
@@ -62,7 +72,8 @@
6272
body: JSON.stringify({
6373
public_key: publicKey,
6474
notes: encryptedNotes,
65-
client_version: '0.2.0'
75+
client_version: '0.2.0',
76+
sync_type: encryptedNotes.length > 0 ? 'full' : 'check' // Indicate if this is just a check
6677
}),
6778
});
6879

@@ -72,6 +83,18 @@
7283
headers: Object.fromEntries(response.headers.entries())
7384
});
7485

86+
// Handle rate limiting with retry-after header
87+
if (response.status === 429) {
88+
const retryAfter = response.headers.get('Retry-After');
89+
const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1000 : 30000;
90+
console.log(`Rate limited. Server requested wait of ${waitTime/1000} seconds`);
91+
92+
// Store the rate limit info for future requests
93+
localStorage.setItem('rate_limit_until', (Date.now() + waitTime).toString());
94+
95+
throw new Error(`Too many requests, please try again after ${waitTime/1000} seconds`);
96+
}
97+
7598
if (!response.ok) {
7699
const errorData = await response.json().catch(() => null);
77100
throw new Error(errorData?.error || `Sync failed with status ${response.status}`);

src/services/webStorage.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export class WebStorageService {
77
private static readonly SETTINGS_KEY = 'sync_settings';
88
private static readonly DELETE_RETENTION_DAYS = 1;
99
private static crypto: CryptoService | null = null;
10+
private static lastSyncTime: number = 0;
11+
private static readonly MIN_SYNC_INTERVAL = 5000; // 5 seconds between syncs
1012

1113
static async initializeCrypto(seedPhrase: string) {
1214
this.crypto = await CryptoService.new(seedPhrase);
@@ -106,6 +108,15 @@ export class WebStorageService {
106108
throw new Error('Crypto not initialized');
107109
}
108110

111+
// Check if we've synced recently
112+
const now = Date.now();
113+
const timeSinceLastSync = now - this.lastSyncTime;
114+
115+
if (timeSinceLastSync < this.MIN_SYNC_INTERVAL) {
116+
console.log(`Throttling sync request. Last sync was ${timeSinceLastSync}ms ago.`);
117+
await new Promise(resolve => setTimeout(resolve, this.MIN_SYNC_INTERVAL - timeSinceLastSync));
118+
}
119+
109120
let lastError: Error | null = null;
110121

111122
for (let attempt = 0; attempt < retries; attempt++) {
@@ -159,12 +170,24 @@ export class WebStorageService {
159170

160171
localStorage.setItem(this.NOTES_KEY, JSON.stringify(mergedNotes));
161172
await this.purgeDeletedNotes();
173+
174+
// Update last sync time on success
175+
this.lastSyncTime = Date.now();
162176
return;
163177

164178
} catch (error) {
165179
console.error(`Sync attempt ${attempt + 1} failed:`, error);
166180
lastError = error as Error;
167181

182+
const errorMessage = error instanceof Error ? error.message : String(error);
183+
184+
if (errorMessage.includes('Too many requests')) {
185+
const waitTime = Math.pow(2, attempt + 2) * 1000;
186+
console.log(`Rate limited. Waiting ${waitTime/1000} seconds before retry...`);
187+
await new Promise(resolve => setTimeout(resolve, waitTime));
188+
continue;
189+
}
190+
168191
if (attempt < retries - 1) {
169192
await new Promise(resolve =>
170193
setTimeout(resolve, Math.pow(2, attempt) * 1000)

0 commit comments

Comments
 (0)