diff --git a/lib/BlindMasterResources/secure_transmissions.dart b/lib/BlindMasterResources/secure_transmissions.dart index 2d2aa98..684bd09 100644 --- a/lib/BlindMasterResources/secure_transmissions.dart +++ b/lib/BlindMasterResources/secure_transmissions.dart @@ -27,7 +27,7 @@ Future secureGet(String path, {Map? queryParame queryParameters: queryParameters?.map((key, value) => MapEntry(key, value.toString())), ); - return await http + var response = await http .get( uri, headers: { @@ -35,7 +35,23 @@ Future secureGet(String path, {Map? queryParame '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 securePost(Map payload, String path) async{ @@ -47,7 +63,7 @@ Future securePost(Map payload, String path) asy path: path, ); - return await http.post( + var response = await http.post( uri, headers: { 'Authorization': 'Bearer $token', @@ -55,7 +71,23 @@ Future securePost(Map payload, String path) asy }, 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 regularGet(String path) async { @@ -63,13 +95,27 @@ Future regularGet(String path) async { path: path, ); - return await http.get( + var response = await http.get( uri, headers: { '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 regularPost(Map payload, String path) async{ @@ -77,13 +123,27 @@ Future regularPost(Map payload, String path) asy path: path, ); - return await http.post( + var response = await http.post( uri, headers: { 'Content-Type': 'application/json', }, 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 connectSocket() async { diff --git a/lib/BlindMasterScreens/Startup/create_user_screen.dart b/lib/BlindMasterScreens/Startup/create_user_screen.dart index eed7a55..7995114 100644 --- a/lib/BlindMasterScreens/Startup/create_user_screen.dart +++ b/lib/BlindMasterScreens/Startup/create_user_screen.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:blind_master/BlindMasterResources/error_snackbar.dart'; import 'package:blind_master/BlindMasterResources/secure_transmissions.dart'; import 'package:flutter/material.dart'; @@ -57,6 +59,11 @@ class _CreateUserScreenState extends State { } } else { 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}'); } diff --git a/lib/BlindMasterScreens/Startup/login_screen.dart b/lib/BlindMasterScreens/Startup/login_screen.dart index 3f04797..b0cee5f 100644 --- a/lib/BlindMasterScreens/Startup/login_screen.dart +++ b/lib/BlindMasterScreens/Startup/login_screen.dart @@ -51,6 +51,11 @@ class _LoginScreenState extends State { final response = await regularPost(payload, 'login'); if (response.statusCode != 200) { 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 {throw Exception('Incorrect email or password');} } diff --git a/lib/BlindMasterScreens/groupControl/create_group_dialog.dart b/lib/BlindMasterScreens/groupControl/create_group_dialog.dart index 8a3e298..11d5a9b 100644 --- a/lib/BlindMasterScreens/groupControl/create_group_dialog.dart +++ b/lib/BlindMasterScreens/groupControl/create_group_dialog.dart @@ -19,7 +19,7 @@ class _CreateGroupDialogState extends State { bool isLoading = true; String? errorMessage; - final bool dev = true; + final bool dev = false; @override void initState() { diff --git a/lib/BlindMasterScreens/groupControl/edit_group_dialog.dart b/lib/BlindMasterScreens/groupControl/edit_group_dialog.dart index 026258a..9351c0b 100644 --- a/lib/BlindMasterScreens/groupControl/edit_group_dialog.dart +++ b/lib/BlindMasterScreens/groupControl/edit_group_dialog.dart @@ -27,8 +27,6 @@ class _EditGroupDialogState extends State { bool isLoading = true; String? errorMessage; - final bool dev = true; - @override void initState() { super.initState(); @@ -91,7 +89,7 @@ class _EditGroupDialogState extends State { } Future _updateGroup() async { - if (selectedPeripheralIds.length < 2 && !dev) { + if (selectedPeripheralIds.length < 2) { setState(() { errorMessage = 'Please select at least 2 blinds'; }); @@ -151,7 +149,7 @@ class _EditGroupDialogState extends State { Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: selectedPeripheralIds.length >= 2 || dev + color: selectedPeripheralIds.length >= 2 ? Theme.of(context).primaryColorLight.withValues(alpha: 0.5) : Colors.orange.withValues(alpha: 0.5), borderRadius: BorderRadius.circular(10), diff --git a/lib/BlindMasterScreens/individualControl/peripheral_screen.dart b/lib/BlindMasterScreens/individualControl/peripheral_screen.dart index 27b65c7..b74a23b 100644 --- a/lib/BlindMasterScreens/individualControl/peripheral_screen.dart +++ b/lib/BlindMasterScreens/individualControl/peripheral_screen.dart @@ -67,6 +67,23 @@ class _PeripheralScreenState extends State { try { socket = await connectSocket(); if (socket == null) throw Exception("Unsuccessful socket connection"); + + // Handle rate limiting errors + socket?.on("error", (data) async { + if (data is Map) { + 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("device_connected", (data) { if (data is Map) {