Files
intaleq_driver/lib/controller/functions/crud.dart
Hamza-Ayed 14484fcd8f 25-10-11/1
2025-10-11 01:25:03 +03:00

879 lines
27 KiB
Dart
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:path/path.dart';
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
import 'package:sefer_driver/controller/functions/network/net_guard.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/controller/auth/captin/login_captin_controller.dart';
import 'package:sefer_driver/main.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:sefer_driver/env/env.dart';
import 'package:sefer_driver/print.dart';
import '../../constant/api_key.dart';
import '../../constant/char_map.dart';
import '../../constant/info.dart';
import '../../views/widgets/error_snakbar.dart';
import 'gemeni.dart';
import 'upload_image.dart';
class CRUD {
final NetGuard _netGuard = NetGuard();
final _client = http.Client();
/// Stores the signature of the last logged error to prevent duplicates.
static String _lastErrorSignature = '';
/// Stores the timestamp of the last logged error.
static DateTime _lastErrorTimestamp =
DateTime(2000); // Initialize with an old date
/// The minimum time that must pass before logging the same error again.
static const Duration _errorLogDebounceDuration = Duration(minutes: 1);
/// Asynchronously logs an error to the server with debouncing to prevent log flooding.
///
/// [error]: A concise description of the error.
/// [details]: Detailed information, such as a stack trace or the server response body.
/// [where]: The location in the code where the error occurred (e.g., 'ClassName.methodName').
static Future<void> 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);
// Fire-and-forget call to prevent infinite loops if the logger itself fails.
CRUD().post(
link: AppLink.addError,
payload: {
'error': error.toString(),
'userId': userId.toString(),
'userType': userType,
'phone': phone.toString(),
'device': where,
'details': details,
},
);
} catch (e) {}
}
/// Centralized private method to handle all API requests.
/// Includes retry logic, network checking, and standardized error handling.
Future<dynamic> _makeRequest({
required String link,
Map<String, dynamic>? payload,
required Map<String, String> headers,
}) async {
// timeouts أقصر
const connectTimeout = Duration(seconds: 6);
const receiveTimeout = Duration(seconds: 10);
Future<http.Response> doPost() {
final url = Uri.parse(link);
// استخدم _client بدل http.post
return _client
.post(url, body: payload, headers: headers)
.timeout(connectTimeout + receiveTimeout);
}
http.Response response;
try {
// retry ذكي: محاولة واحدة إضافية فقط لأخطاء شبكة/5xx
try {
response = await doPost();
} on SocketException catch (_) {
// محاولة ثانية واحدة فقط
response = await doPost();
} on TimeoutException catch (_) {
response = await doPost();
}
final sc = response.statusCode;
final body = response.body;
// 2xx
if (sc >= 200 && sc < 300) {
try {
final jsonData = jsonDecode(body);
return jsonData; // لا تعيد 'success' فقط؛ أعِد الجسم كله
} catch (e, st) {
// لا تسجّل كخطأ شبكي لكل حالة؛ فقط معلومات
addError('JSON Decode Error', 'Body: $body\n$st',
'CRUD._makeRequest $link');
return 'failure';
}
}
// 401 → دع الطبقة العليا تتعامل مع التجديد
if (sc == 401) {
// لا تستدع getJWT هنا كي لا نضاعف الرحلات
return 'token_expired';
}
// 5xx: لا تعِد المحاولة هنا (حاولنا مرة ثانية فوق)
if (sc >= 500) {
addError(
'Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link');
return 'failure';
}
// 4xx أخرى: أعد الخطأ بدون تسجيل مكرر
return 'failure';
} on SocketException {
_netGuard.notifyOnce((title, msg) => mySnackeBarError(msg));
return 'no_internet';
} on TimeoutException {
return 'failure';
} catch (e, st) {
addError('HTTP Request Exception: $e', 'Stack: $st',
'CRUD._makeRequest $link');
return 'failure';
}
}
/// Performs a standard authenticated POST request.
/// Automatically handles token renewal.
Future<dynamic> post({
required String link,
Map<String, dynamic>? payload,
}) async {
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
// if (JwtDecoder.isExpired(token)) {
// await Get.put(LoginController()).getJWT();
// token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
// }
final headers = {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $token'
};
return await _makeRequest(
link: link,
payload: payload,
headers: headers,
);
}
/// Performs a standard authenticated GET request (using POST method as per original code).
/// Automatically handles token renewal.
Future<dynamic> get({
required String link,
Map<String, dynamic>? payload,
}) async {
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Bearer ${r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]}'
},
);
Log.print('response: ${response.body}');
Log.print('req: ${response.request}');
Log.print('payload: ${payload}');
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return response.body;
}
return jsonData['status'];
} else if (response.statusCode == 401) {
// Specifically handle 401 Unauthorized
var jsonData = jsonDecode(response.body);
if (jsonData['error'] == 'Token expired') {
// Show snackbar prompting to re-login
await Get.put(LoginDriverController()).getJWT();
// mySnackbarSuccess('please order now'.tr);
return 'token_expired'; // Return a specific value for token expiration
} else {
// Other 401 errors
addError('Unauthorized: ${jsonData['error']}', 'crud().post - 401',
url.toString());
return 'failure';
}
} else {
addError('Non-200 response code: ${response.statusCode}',
'crud().post - Other', url.toString());
return 'failure';
}
}
/// Performs an authenticated POST request to wallet endpoints.
Future<dynamic> postWallet({
required String link,
Map<String, dynamic>? payload,
}) async {
var jwt = await LoginDriverController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
final headers = {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $jwt',
'X-HMAC-Auth': hmac.toString(),
};
return await _makeRequest(
link: link,
payload: payload,
headers: headers,
);
}
/// Performs an authenticated GET request to wallet endpoints (using POST).
Future<dynamic> getWallet({
required String link,
Map<String, dynamic>? payload,
}) async {
var s = await LoginDriverController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $s',
'X-HMAC-Auth': hmac.toString(),
},
);
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return response.body;
}
return jsonData['status'];
} else if (response.statusCode == 401) {
// Specifically handle 401 Unauthorized
var jsonData = jsonDecode(response.body);
if (jsonData['error'] == 'Token expired') {
// Show snackbar prompting to re-login
await Get.put(LoginDriverController()).getJwtWallet();
return 'token_expired'; // Return a specific value for token expiration
} else {
// Other 401 errors
addError('Unauthorized: ${jsonData['error']}', 'crud().post - 401',
url.toString());
return 'failure';
}
} else {
addError('Non-200 response code: ${response.statusCode}',
'crud().post - Other', url.toString());
return 'failure';
}
}
Future<dynamic> postWalletMtn(
{required String link, Map<String, dynamic>? payload}) async {
final s = await LoginDriverController().getJwtWallet();
final hmac = box.read(BoxName.hmac);
final url = Uri.parse(link);
try {
final response = await http.post(
url,
body: payload, // form-urlencoded مناسب لـ filterRequest
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer $s",
"X-HMAC-Auth": hmac.toString(),
},
);
Map<String, dynamic> wrap(String status, {Object? message, int? code}) {
return {
'status': status,
'message': message,
'code': code ?? response.statusCode,
};
}
if (response.statusCode == 200) {
try {
final jsonData = jsonDecode(response.body);
// نتوقع الآن شكل موحّد من السيرفر: {status, message, data?}
return jsonData;
} catch (e) {
return wrap('failure',
message: 'JSON decode error', code: response.statusCode);
}
} else if (response.statusCode == 401) {
try {
final jsonData = jsonDecode(response.body);
if (jsonData is Map && jsonData['error'] == 'Token expired') {
await Get.put(LoginDriverController()).getJWT();
return {
'status': 'failure',
'message': 'token_expired',
'code': 401
};
}
return wrap('failure', message: jsonData);
} catch (_) {
return wrap('failure', message: response.body);
}
} else {
// غير 200 ارجع التفاصيل
try {
final jsonData = jsonDecode(response.body);
return wrap('failure', message: jsonData);
} catch (_) {
return wrap('failure', message: response.body);
}
}
} catch (e) {
return {
'status': 'failure',
'message': 'HTTP request error: $e',
'code': -1
};
}
}
// Future<dynamic> postWallet(
// {required String link, Map<String, dynamic>? payload}) async {
// var s = await LoginDriverController().getJwtWallet();
// final hmac = box.read(BoxName.hmac);
// var url = Uri.parse(link);
// try {
// // await LoginDriverController().getJWT();
// var response = await http.post(
// url,
// body: payload,
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// 'Authorization': 'Bearer $s',
// 'X-HMAC-Auth': hmac.toString(),
// },
// );
// if (response.statusCode == 200) {
// try {
// var jsonData = jsonDecode(response.body);
// if (jsonData['status'] == 'success') {
// return jsonData;
// } else {
// return jsonData['status'];
// }
// } catch (e) {
// // addError(e.toString(), 'crud().post - JSON decoding');
// return 'failure';
// }
// } else if (response.statusCode == 401) {
// // Specifically handle 401 Unauthorized
// var jsonData = jsonDecode(response.body);
// if (jsonData['error'] == 'Token expired') {
// return 'token_expired'; // Return a specific value for token expiration
// } else {
// // Other 401 errors
// // addError('Unauthorized: ${jsonData['error']}', 'crud().post - 401');
// return 'failure';
// }
// } else {
// // addError('Non-200 response code: ${response.statusCode}',
// // 'crud().post - Other');
// return 'failure';
// }
// } catch (e) {
// // addError('HTTP request error: $e', 'crud().post - HTTP');
// return 'failure';
// }
// }
// Future<dynamic> post(
// {required String link, Map<String, dynamic>? payload}) async {
// var url = Uri.parse(link);
// try {
// bool isTokenExpired = JwtDecoder.isExpired(X
// .r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs)
// .toString()
// .split(AppInformation.addd)[0]);
// if (isTokenExpired) {
// await LoginDriverController().getJWT();
// }
// var response = await http.post(
// url,
// body: payload,
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// 'Authorization':
// 'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}'
// // 'Authorization': 'Bearer ${box.read(BoxName.jwt)}'
// },
// );
// if (response.statusCode == 200) {
// try {
// var jsonData = jsonDecode(response.body);
// if (jsonData['status'] == 'success') {
// return jsonData;
// } else {
// return jsonData['status'];
// }
// } catch (e) {
// // addError(e.toString(), url);
// return 'failure';
// }
// } else if (response.statusCode == 401) {
// // Specifically handle 401 Unauthorized
// var jsonData = jsonDecode(response.body);
// if (jsonData['error'] == 'Token expired') {
// // Show snackbar prompting to re-login
// // await Get.put(LoginDriverController()).getJWT();
// // MyDialog().getDialog(
// // 'Session expired. Please log in again.'.tr,
// // '',
// // () {
// // Get.put(LoginController()).loginUsingCredentials(
// // box.read(BoxName.passengerID), box.read(BoxName.email));
// // Get.back();
// // },
// // );
// return 'token_expired'; // Return a specific value for token expiration
// } else {
// // Other 401 errors
// // addError('Unauthorized: ${jsonData['error']}', 'crud().post - 401');
// return 'failure';
// }
// } else {
// // addError('Non-200 response code: ${response.statusCode}',
// // 'crud().post - Other');
// return 'failure';
// }
// } catch (e) {
// // addError('HTTP request error: $e', 'crud().post - HTTP');
// return 'failure';
// }
// }
Future<dynamic> getAgoraToken({
required String channelName,
required String uid,
}) async {
var uid = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
var res = await http.get(Uri.parse(
// 'https://repulsive-pig-rugby-shirt.cyclic.app/token?channelName=$channelName'),
'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'];
} else {}
}
Future<dynamic> 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'
// 'Authorization': 'Bearer ${Env.llamaKey}'
};
var data = json.encode({
"model": "Llama-3-70b-Inst-FW",
// "model": "llama-13b-chat",
"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 http.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));
var extractedString =
await arabicTextExtractByVisionAndAI(imagePath: imagePath);
var json = jsonDecode(extractedString);
var textValues = extractTextFromLines(json);
// await Get.put(AI()).geminiAiExtraction(prompt, textValues);
await Get.put(AI()).anthropicAI(textValues, prompt, imagePath);
}
String extractTextFromLines(Map<String, dynamic> jsonData) {
final readResult = jsonData['readResult'];
final blocks = readResult['blocks'];
final StringBuffer buffer = StringBuffer();
for (final block in blocks) {
final lines = block['lines'];
for (final line in lines) {
final text = line['text'];
buffer.write(text);
buffer.write('\n');
}
}
return buffer.toString().trim();
}
Future<dynamic> arabicTextExtractByVisionAndAI({
required String imagePath,
}) async {
var headers = {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': AK.ocpApimSubscriptionKey
};
String imagePathFull =
'${AppLink.server}/card_image/$imagePath-${box.read(BoxName.driverID)}.jpg';
var request = http.Request('POST', Uri.parse(
// 'https://ocrhamza.cognitiveservices.azure.com/vision/v2.1/ocr?language=ar'));
// 'https://eastus.api.cognitive.microsoft.com/vision/v3.2/ocr'
'https://eastus.api.cognitive.microsoft.com/computervision/imageanalysis:analyze?features=caption,read&model-version=latest&language=en&api-version=2024-02-01'));
request.body = json.encode({"url": imagePathFull});
// request.body = json.encode({"url": imagePathFull});
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
// 'response.stream.bytesToString(): ${await response.stream.bytesToString()}');
return await response.stream.bytesToString();
} else {}
}
Future<dynamic> 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 http.post(
url,
body: data,
headers: headers,
);
if (response.statusCode == 200) {
return response.body;
}
return response.statusCode;
}
Future<dynamic> postStripe({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? secretKey = await storage.read(key: BoxName.secretKey);
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer ${AK.secretKeyStripe}',
},
);
if (response.statusCode == 200) {
return response.body;
} else {}
}
Future<dynamic> postPayMob({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse(
link,
);
var response = await http.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;
} else {
return (jsonData['status']);
}
} else {
return response.statusCode;
}
}
Future<void> sendEmail(String link, Map<String, String>? payload) async {
// التحقق من صلاحية التوكن
String rawJwt = box.read(BoxName.jwt);
String token = X
.r(X.r(X.r(rawJwt, cn), cC), cs)
.toString()
.split(AppInformation.addd)[0];
bool isTokenExpired = JwtDecoder.isExpired(token);
if (isTokenExpired) {
await LoginDriverController().getJWT();
rawJwt = box.read(BoxName.jwt); // تحديث التوكن بعد التجديد
token = X
.r(X.r(X.r(rawJwt, cn), cC), cs)
.toString()
.split(AppInformation.addd)[0];
}
// إعداد الهيدر
final headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer $token",
};
// إعداد الطلب
final request = http.Request('POST', Uri.parse(link));
request.bodyFields = payload ?? {};
request.headers.addAll(headers);
// إرسال الطلب
final response = await request.send();
// التحقق من النتيجة
if (response.statusCode == 200) {
} else {
final responseBody = await response.stream.bytesToString();
}
}
Future<dynamic> postFromDialogue({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse(
link,
);
var response = await http.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();
// Get.snackbar(
// jsonData['status'],
// jsonData['message'],
// );
return response.body;
}
}
return (jsonData['status']);
}
}
Future<void> 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');
// Send the verification request
final response = await http.post(
verificationUri,
headers: {
'Authorization':
'Basic ' + base64Encode(utf8.encode('$accountSid:$authToken')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
'To': phoneNumber,
'Channel': 'sms',
},
);
if (response.statusCode == 201) {
} else {}
// Prompt the user to enter the OTP
final otpCode = "123456"; // Replace with user input
// Check the verification code
final checkUri = Uri.parse(
'https://verify.twilio.com/v2/Services/$verifySid/VerificationCheck');
final checkResponse = await http.post(
checkUri,
headers: {
'Authorization':
'Basic ' + base64Encode(utf8.encode('$accountSid:$authToken')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
'To': phoneNumber,
'Code': otpCode,
},
);
if (checkResponse.statusCode == 201) {
} else {}
}
Future<dynamic> getGoogleApi({
required String link,
Map<String, dynamic>? payload,
}) async {
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
);
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'OK') {
return jsonData;
}
return (jsonData['status']);
}
Future<dynamic> update({
required String endpoint,
required Map<String, dynamic> data,
required String id,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse('$endpoint/$id');
var response = await http.put(
url,
body: json.encode(data),
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
return json.decode(response.body);
}
Future<dynamic> delete({
required String endpoint,
required String id,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse('$endpoint/$id');
var response = await http.delete(
url,
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
return json.decode(response.body);
}
}
/// Custom exception for when there is no internet connection.
class NoInternetException implements Exception {
final String message;
NoInternetException(
[this.message =
'No internet connection. Please check your network and try again.']);
@override
String toString() => message;
}
/// Custom exception for when the network is too slow (request times out).
class WeakNetworkException implements Exception {
final String message;
WeakNetworkException(
[this.message =
'Your network connection is too slow. Please try again later.']);
@override
String toString() => message;
}
class ApiException implements Exception {
final String message;
final int? statusCode;
ApiException(this.message, [this.statusCode]);
@override
String toString() =>
"ApiException: $message (Status Code: ${statusCode ?? 'N/A'})";
}