import 'dart:async'; import 'dart:convert'; import 'package:siro_rider/constant/box_name.dart'; import 'package:siro_rider/constant/links.dart'; import 'package:siro_rider/controller/auth/login_controller.dart'; import 'package:siro_rider/main.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:siro_rider/env/env.dart'; import '../../constant/api_key.dart'; import '../../print.dart'; import '../../views/widgets/elevated_btn.dart'; import '../../views/widgets/error_snakbar.dart'; import 'encrypt_decrypt.dart'; import 'upload_image.dart'; import 'dart:io'; import 'network/net_guard.dart'; import 'ssl_pinning.dart'; class CRUD { final NetGuard _netGuard = NetGuard(); final _client = SslPinning.createPinnedClient(); static bool _isRefreshingJWT = false; static String _lastErrorSignature = ''; static DateTime _lastErrorTimestamp = DateTime(2000); static const Duration _errorLogDebounceDuration = Duration(minutes: 1); /// JWT validity check without external libraries. static bool _isJwtValid(String? token) { if (token == null || token.isEmpty) return false; try { final parts = token.split('.'); if (parts.length != 3) return false; String payload = parts[1]; switch (payload.length % 4) { case 2: payload += '=='; break; case 3: payload += '='; break; } final decoded = jsonDecode(utf8.decode(base64Url.decode(payload))); final exp = decoded['exp']; if (exp == null) return false; return DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000); } catch (_) { return false; } } static Future addError( String error, String details, String where) async { try { final currentErrorSignature = '$where-$error'; final now = DateTime.now(); if (currentErrorSignature == _lastErrorSignature && now.difference(_lastErrorTimestamp) < _errorLogDebounceDuration) { return; } _lastErrorSignature = currentErrorSignature; _lastErrorTimestamp = now; final userId = box.read(BoxName.driverID) ?? box.read(BoxName.passengerID); final userType = box.read(BoxName.driverID) != null ? 'Driver' : 'Passenger'; final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver); Log.print( "🚨 [ADD_ERROR] Where: $where | Error: $error | Details: $details"); CRUD().post( link: AppLink.addError, payload: { 'error': error.toString(), 'userId': userId.toString(), 'userType': userType, 'phone': phone.toString(), 'device': where, 'details': details, }, ); } catch (e) { Log.print("Error occurred: $e"); } } String _getFpHeader() { return box.read(BoxName.deviceFpEncrypted)?.toString() ?? ''; } Future _getJwt() async { try { final String? encryptedJwt = await storage.read(key: BoxName.jwt); if (encryptedJwt == null || encryptedJwt.isEmpty) { final String? fallback = box.read(BoxName.jwt); if (fallback != null) { return r(fallback).toString().split(Env.addd)[0]; } return ''; } return r(encryptedJwt).toString().split(Env.addd)[0]; } catch (e) { Log.print('Error reading JWT from SecureStorage: $e'); final String? fallback = box.read(BoxName.jwt); if (fallback != null) { return r(fallback).toString().split(Env.addd)[0]; } return ''; } } /// Centralized request handler with retry for weak networks. /// For Syria (3G): 60s total timeout, 3 retries, exponential backoff. Future _makeRequest({ required String link, Map? payload, required Map headers, }) async { const totalTimeout = Duration(seconds: 60); Future doPost() { final url = Uri.parse(link); return _client .post(url, body: payload, headers: headers) .timeout(totalTimeout); } http.Response? response; int attempts = 0; while (attempts < 3) { try { attempts++; response = await doPost(); break; } on SocketException catch (_) { Log.print('⚠️ SocketException attempt $attempts β€” $link'); if (attempts >= 3) { _netGuard.notifyOnce((title, msg) => mySnackeBarError(msg)); return 'no_internet'; } await Future.delayed(Duration(seconds: attempts)); } on TimeoutException catch (_) { Log.print('⚠️ TimeoutException attempt $attempts β€” $link'); if (attempts >= 3) return 'failure'; } catch (e) { if (e.toString().contains('errno = 9') && attempts < 3) { await Future.delayed(const Duration(milliseconds: 500)); continue; } addError( 'HTTP Exception: $e', 'Try: $attempts', 'CRUD._makeRequest $link'); return 'failure'; } } if (response == null) return 'failure'; final sc = response.statusCode; final body = response.body; Log.print('request: ${response.request}'); Log.print('body: $body'); Log.print('payload: $payload'); if (sc >= 200 && sc < 300) { try { return jsonDecode(body); } catch (e, st) { addError('JSON Decode Error', 'Body: $body\n$st', 'CRUD._makeRequest $link'); return 'failure'; } } if (sc == 401) { final isNonCritical = link.contains('errorApp.php'); if (!_isRefreshingJWT && !isNonCritical) { _isRefreshingJWT = true; try { await Get.put(LoginController()).getJWT(); } finally { _isRefreshingJWT = false; } } return 'token_expired'; } if (sc >= 500) { addError( 'Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link'); return 'failure'; } return 'failure'; } Future post({ required String link, Map? payload, }) async { String token = await _getJwt(); final headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Bearer $token', 'X-Device-FP': _getFpHeader(), }; return await _makeRequest(link: link, payload: payload, headers: headers); } Future get({ required String link, Map? payload, }) async { String token = await _getJwt(); final headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Bearer $token', 'X-Device-FP': _getFpHeader(), }; return await _makeRequest(link: link, payload: payload, headers: headers); } // ═══════════════════════════════════════════════════════════════ // postWallet β€” Ψ·Ω„Ψ¨ POST Ω„Ψ³ΩŠΨ±ΩΨ± Ψ§Ω„Ω…Ψ―ΩΩˆΨΉΨ§Ψͺ // ─────────────────────────────────────────────────────────────── // Ψ§Ω„Ψͺغيير: Ψ₯آافة X-Device-FP header // 3 headers Ω…ΨΉΨ§Ω‹: JWT + HMAC + FP // ═══════════════════════════════════════════════════════════════ Future postWallet({ required String link, Map? payload, }) async { var jwt = await LoginController().getJwtWallet(); final hmac = box.read(BoxName.hmac); final headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Bearer $jwt', 'X-HMAC-Auth': hmac.toString(), 'X-Device-FP': _getFpHeader(), }; Log.print('headers: $headers'); Log.print('payload: $payload'); Log.print('link: $link'); return await _makeRequest(link: link, payload: payload, headers: headers); } Future getWallet({ required String link, Map? payload, }) async { var s = await LoginController().getJwtWallet(); final hmac = box.read(BoxName.hmac); final headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Bearer $s', 'X-HMAC-Auth': hmac.toString(), 'X-Device-FP': _getFpHeader(), }; return await _makeRequest(link: link, payload: payload, headers: headers); } // ======================================================================= // All other specialized methods remain below unchanged. // They interact with external third-party APIs and have unique // authentication or body structures that don't need the FP header. // ======================================================================= Future postWalletMtn( {required String link, Map? payload}) async { final s = await LoginController().getJwtWallet(); final hmac = box.read(BoxName.hmac); final headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Bearer $s', 'X-HMAC-Auth': hmac.toString(), 'X-Device-FP': _getFpHeader(), }; final result = await _makeRequest(link: link, payload: payload, headers: headers); if (result is Map || result is List) return result; if (result == 'no_internet') { return {'status': 'failure', 'message': 'no_internet', 'code': -1}; } return result; } Future sendWhatsAppAuth(String to, String token) async { var res = await CRUD() .get(link: AppLink.getApiKey, payload: {'keyName': 'whatsapp_key'}); var accesstoken = jsonDecode(res)['message']['whatsapp_key']; var headers = { 'Authorization': 'Bearer $accesstoken', 'Content-Type': 'application/json' }; var url = 'https://graph.facebook.com/v20.0/${Env.whatappID}/messages'; var request = http.Request('POST', Uri.parse(url)); var body = json.encode({ "messaging_product": "whatsapp", "to": to, "type": "template", "template": { "name": "sefer1", "language": {"code": "en"}, "components": [ { "type": "body", "parameters": [ {"type": "text", "text": token} ] } ] } }); request.body = body; request.headers.addAll(headers); try { http.StreamedResponse response = await request.send(); if (response.statusCode == 200) { String responseBody = await response.stream.bytesToString(); Get.defaultDialog( title: 'You will receive a code in WhatsApp Messenger'.tr, middleText: 'wait 1 minute to recive message'.tr, confirm: MyElevatedButton( title: 'OK'.tr, onPressed: () => Get.back(), ), ); } else { String errorBody = await response.stream.bytesToString(); } } catch (e) { Log.print("Error occurred: $e"); } } Future getAgoraToken({ required String channelName, required String uid, }) async { var uid = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver); var res = await _client.get( Uri.parse( 'https://orca-app-b2i85.ondigitalocean.app/token?channelName=$channelName'), headers: {'Authorization': 'Bearer ${AK.agoraAppCertificate}'}, ); if (res.statusCode == 200) { var response = jsonDecode(res.body); return response['token']; } } Future getLlama({ required String link, required String payload, required String prompt, }) async { var url = Uri.parse(link); var headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer LL-X5lJ0Px9CzKK0HTuVZ3u2u4v3tGWkImLTG7okGRk4t25zrsLqJ0qNoUzZ2x4ciPy' }; var data = json.encode({ "model": "Llama-3-70b-Inst-FW", "messages": [ { "role": "user", "content": "Extract the desired information from the following passage as json decoded like $prompt just in this:\n\n$payload" } ], "temperature": 0.9 }); var response = await _client.post(url, body: data, headers: headers); if (response.statusCode == 200) return response.body; return response.statusCode; } Future allMethodForAI(String prompt, linkPHP, imagePath) async { await ImageController().choosImage(linkPHP, imagePath); Future.delayed(const Duration(seconds: 2)); String extracted = await arabicTextExtractByVisionAndAI(imagePath: imagePath); } Future arabicTextExtractByVisionAndAI({ required String imagePath, }) async { var headers = { 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': '21010e54b50f41a4904708c526e102df' }; var url = Uri.parse( 'https://ocrhamza.cognitiveservices.azure.com/vision/v2.1/ocr?language=ar'); String imagePathFull = '${AppLink.server}card_image/$imagePath-${box.read(BoxName.driverID) ?? box.read(BoxName.passengerID)}.jpg'; var requestBody = {"url": imagePathFull}; var response = await _client.post(url, body: jsonEncode(requestBody), headers: headers); if (response.statusCode == 200) { var responseBody = jsonDecode(response.body); return responseBody.toString(); } return response.statusCode; } Future getChatGPT({ required String link, required String payload, }) async { var url = Uri.parse(link); var headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ${Env.chatGPTkeySeferNew}' }; var data = json.encode({ "model": "gpt-3.5-turbo", "messages": [ { "role": "user", "content": "Extract the desired information from the following passage as json decoded like vin,make,made,year,expiration_date,color,owner,registration_date just in this:\n\n$payload" } ], "temperature": 0.9 }); var response = await _client.post(url, body: data, headers: headers); if (response.statusCode == 200) return response.body; return response.statusCode; } Future postPayMob({ required String link, Map? payload, }) async { var url = Uri.parse(link); var response = await _client.post(url, body: payload, headers: {'Content-Type': 'application/json'}); var jsonData = jsonDecode(response.body); if (response.statusCode == 200) { if (jsonData['status'] == 'success') return response.body; return jsonData['status']; } else { return response.statusCode; } } sendEmail(String link, Map? payload) async { var headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}', }; var request = http.Request('POST', Uri.parse(link)); request.bodyFields = payload!; request.headers.addAll(headers); await request.send(); } Future postFromDialogue({ required String link, Map? payload, }) async { var url = Uri.parse(link); var response = await _client.post( url, body: payload, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}', }, ); if (response.body.isNotEmpty) { var jsonData = jsonDecode(response.body); if (response.statusCode == 200) { if (jsonData['status'] == 'success') { Get.back(); return response.body; } } return jsonData['status']; } } Future sendVerificationRequest(String phoneNumber) async { final accountSid = AK.accountSIDTwillo; final authToken = AK.authTokenTwillo; final verifySid = AK.twilloRecoveryCode; final Uri verificationUri = Uri.parse( 'https://verify.twilio.com/v2/Services/$verifySid/Verifications'); await _client.post( verificationUri, headers: { 'Authorization': 'Basic ' + base64Encode(utf8.encode('$accountSid:$authToken')), 'Content-Type': 'application/x-www-form-urlencoded', }, body: {'To': phoneNumber, 'Channel': 'sms'}, ); final otpCode = "123456"; final checkUri = Uri.parse( 'https://verify.twilio.com/v2/Services/$verifySid/VerificationCheck'); final checkResponse = await _client.post( checkUri, headers: { 'Authorization': 'Basic ' + base64Encode(utf8.encode('$accountSid:$authToken')), 'Content-Type': 'application/x-www-form-urlencoded', }, body: {'To': phoneNumber, 'Code': otpCode}, ); } Future getGoogleApi({ required String link, Map? payload, }) async { var url = Uri.parse(link); var response = await _client.post(url, body: payload); var jsonData = jsonDecode(response.body); if (jsonData['status'] == 'OK') return jsonData; return jsonData['status']; } Future getHereMap({required String link}) async { var url = Uri.parse(link); try { var response = await _client.get(url); if (response.statusCode == 200) { var decodedBody = utf8.decode(response.bodyBytes); return jsonDecode(decodedBody); } return null; } catch (e) { return null; } } Future getMapSaas({ required String link, }) async { var url = Uri.parse(link); try { var response = await _client.get( url, headers: { 'Content-Type': 'application/json', 'x-api-key': Env.mapSaasKey, }, ); Log.print('link -MapSaas: $link'); Log.print('response -MapSaas: ${response.body}'); if (response.statusCode == 200) { return jsonDecode(response.body); } Log.print('MapSaas Error: ${response.statusCode} - ${response.body}'); return null; } catch (e) { Log.print('MapSaas Exception: $e'); return null; } } Future postMapSaas({ required String link, required Map payload, }) async { var url = Uri.parse(link); try { var response = await _client.post( url, body: jsonEncode(payload), headers: { 'Content-Type': 'application/json', 'x-api-key': Env.mapSaasKey, }, ); Log.print('post -MapSaas link: $link'); Log.print('post -MapSaas payload: $payload'); Log.print('post -MapSaas response: ${response.body}'); if (response.statusCode == 200 || response.statusCode == 201) { return jsonDecode(response.body); } Log.print( 'MapSaas Post Error: ${response.statusCode} - ${response.body}'); return null; } catch (e) { Log.print('MapSaas Post Exception: $e'); return null; } } }