rate limit handling
This commit is contained in:
@@ -27,7 +27,7 @@ Future<http.Response?> secureGet(String path, {Map<String, dynamic>? queryParame
|
|||||||
queryParameters: queryParameters?.map((key, value) => MapEntry(key, value.toString())),
|
queryParameters: queryParameters?.map((key, value) => MapEntry(key, value.toString())),
|
||||||
);
|
);
|
||||||
|
|
||||||
return await http
|
var response = await http
|
||||||
.get(
|
.get(
|
||||||
uri,
|
uri,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -35,7 +35,23 @@ Future<http.Response?> secureGet(String path, {Map<String, dynamic>? queryParame
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.timeout(const Duration(seconds: 10)); // 🚀 Timeout added
|
.timeout(const Duration(seconds: 10));
|
||||||
|
|
||||||
|
// Retry once if rate limited
|
||||||
|
if (response.statusCode == 429) {
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
response = await http
|
||||||
|
.get(
|
||||||
|
uri,
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.timeout(const Duration(seconds: 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response?> securePost(Map<String, dynamic> payload, String path) async{
|
Future<http.Response?> securePost(Map<String, dynamic> payload, String path) async{
|
||||||
@@ -47,7 +63,7 @@ Future<http.Response?> securePost(Map<String, dynamic> payload, String path) asy
|
|||||||
path: path,
|
path: path,
|
||||||
);
|
);
|
||||||
|
|
||||||
return await http.post(
|
var response = await http.post(
|
||||||
uri,
|
uri,
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer $token',
|
'Authorization': 'Bearer $token',
|
||||||
@@ -55,7 +71,23 @@ Future<http.Response?> securePost(Map<String, dynamic> payload, String path) asy
|
|||||||
},
|
},
|
||||||
body: json.encode(payload),
|
body: json.encode(payload),
|
||||||
)
|
)
|
||||||
.timeout(const Duration(seconds: 10)); // 🚀 Timeout added
|
.timeout(const Duration(seconds: 10));
|
||||||
|
|
||||||
|
// Retry once if rate limited
|
||||||
|
if (response.statusCode == 429) {
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
response = await http.post(
|
||||||
|
uri,
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: json.encode(payload),
|
||||||
|
)
|
||||||
|
.timeout(const Duration(seconds: 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response> regularGet(String path) async {
|
Future<http.Response> regularGet(String path) async {
|
||||||
@@ -63,13 +95,27 @@ Future<http.Response> regularGet(String path) async {
|
|||||||
path: path,
|
path: path,
|
||||||
);
|
);
|
||||||
|
|
||||||
return await http.get(
|
var response = await http.get(
|
||||||
uri,
|
uri,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.timeout(const Duration(seconds: 10)); // 🚀 Timeout added
|
.timeout(const Duration(seconds: 10));
|
||||||
|
|
||||||
|
// Retry once if rate limited
|
||||||
|
if (response.statusCode == 429) {
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
response = await http.get(
|
||||||
|
uri,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.timeout(const Duration(seconds: 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response> regularPost(Map<String, dynamic> payload, String path) async{
|
Future<http.Response> regularPost(Map<String, dynamic> payload, String path) async{
|
||||||
@@ -77,13 +123,27 @@ Future<http.Response> regularPost(Map<String, dynamic> payload, String path) asy
|
|||||||
path: path,
|
path: path,
|
||||||
);
|
);
|
||||||
|
|
||||||
return await http.post(
|
var response = await http.post(
|
||||||
uri,
|
uri,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: json.encode(payload)
|
body: json.encode(payload)
|
||||||
).timeout(const Duration(seconds: 10)); // 🚀 Timeout added
|
).timeout(const Duration(seconds: 10));
|
||||||
|
|
||||||
|
// Retry once if rate limited
|
||||||
|
if (response.statusCode == 429) {
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
response = await http.post(
|
||||||
|
uri,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: json.encode(payload)
|
||||||
|
).timeout(const Duration(seconds: 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<IO.Socket?> connectSocket() async {
|
Future<IO.Socket?> connectSocket() async {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:blind_master/BlindMasterResources/error_snackbar.dart';
|
import 'package:blind_master/BlindMasterResources/error_snackbar.dart';
|
||||||
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
|
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -57,6 +59,11 @@ class _CreateUserScreenState extends State<CreateUserScreen> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (response.statusCode == 409) throw Exception('Email Already In Use!');
|
if (response.statusCode == 409) throw Exception('Email Already In Use!');
|
||||||
|
if (response.statusCode == 429) {
|
||||||
|
final body = json.decode(response.body);
|
||||||
|
final retryAfter = body['retryAfter'] ?? 'some time';
|
||||||
|
throw Exception('Too many account creation attempts. Please try again in $retryAfter minutes.');
|
||||||
|
}
|
||||||
throw Exception('Create failed: ${response.statusCode}');
|
throw Exception('Create failed: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
final response = await regularPost(payload, 'login');
|
final response = await regularPost(payload, 'login');
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
if (response.statusCode == 400) {throw Exception('Email and Password Necessary');}
|
if (response.statusCode == 400) {throw Exception('Email and Password Necessary');}
|
||||||
|
else if (response.statusCode == 429) {
|
||||||
|
final body = json.decode(response.body);
|
||||||
|
final retryAfter = body['retryAfter'] ?? 'some time';
|
||||||
|
throw Exception('Too many login attempts. Please try again in $retryAfter minutes.');
|
||||||
|
}
|
||||||
else if (response.statusCode == 500) {throw Exception('Server Error');}
|
else if (response.statusCode == 500) {throw Exception('Server Error');}
|
||||||
else {throw Exception('Incorrect email or password');}
|
else {throw Exception('Incorrect email or password');}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class _CreateGroupDialogState extends State<CreateGroupDialog> {
|
|||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
String? errorMessage;
|
String? errorMessage;
|
||||||
|
|
||||||
final bool dev = true;
|
final bool dev = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ class _EditGroupDialogState extends State<EditGroupDialog> {
|
|||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
String? errorMessage;
|
String? errorMessage;
|
||||||
|
|
||||||
final bool dev = true;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -91,7 +89,7 @@ class _EditGroupDialogState extends State<EditGroupDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateGroup() async {
|
Future<void> _updateGroup() async {
|
||||||
if (selectedPeripheralIds.length < 2 && !dev) {
|
if (selectedPeripheralIds.length < 2) {
|
||||||
setState(() {
|
setState(() {
|
||||||
errorMessage = 'Please select at least 2 blinds';
|
errorMessage = 'Please select at least 2 blinds';
|
||||||
});
|
});
|
||||||
@@ -151,7 +149,7 @@ class _EditGroupDialogState extends State<EditGroupDialog> {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: selectedPeripheralIds.length >= 2 || dev
|
color: selectedPeripheralIds.length >= 2
|
||||||
? Theme.of(context).primaryColorLight.withValues(alpha: 0.5)
|
? Theme.of(context).primaryColorLight.withValues(alpha: 0.5)
|
||||||
: Colors.orange.withValues(alpha: 0.5),
|
: Colors.orange.withValues(alpha: 0.5),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
|||||||
@@ -67,6 +67,23 @@ class _PeripheralScreenState extends State<PeripheralScreen> {
|
|||||||
try {
|
try {
|
||||||
socket = await connectSocket();
|
socket = await connectSocket();
|
||||||
if (socket == null) throw Exception("Unsuccessful socket connection");
|
if (socket == null) throw Exception("Unsuccessful socket connection");
|
||||||
|
|
||||||
|
// Handle rate limiting errors
|
||||||
|
socket?.on("error", (data) async {
|
||||||
|
if (data is Map<String, dynamic>) {
|
||||||
|
if (data['code'] == 429) {
|
||||||
|
// Rate limited - wait and reconnect
|
||||||
|
print("Rate limited: ${data['message']}. Reconnecting in 1 second...");
|
||||||
|
socket?.disconnect();
|
||||||
|
socket?.dispose();
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
if (mounted) {
|
||||||
|
initSocket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
socket?.on("success", (_) {
|
socket?.on("success", (_) {
|
||||||
socket?.on("device_connected", (data) {
|
socket?.on("device_connected", (data) {
|
||||||
if (data is Map<String, dynamic>) {
|
if (data is Map<String, dynamic>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user