Update: 2026-06-12 01:23:54
This commit is contained in:
@@ -264,12 +264,23 @@ class RegistrationController extends GetxController {
|
||||
|
||||
/// خريطة لتخزين روابط المستندات بعد الرفع
|
||||
final Map<String, String> docUrls = {
|
||||
'driver_license_front': '',
|
||||
'id_front': '',
|
||||
'id_back': '',
|
||||
'driver_license': '',
|
||||
'driver_license_back': '',
|
||||
'profile_picture': '',
|
||||
'criminal_record': '',
|
||||
'car_license_front': '',
|
||||
'car_license_back': '',
|
||||
};
|
||||
|
||||
String getCriminalRecordTitle() {
|
||||
String currentCountry = box.read(BoxName.countryCode) ?? 'Jordan';
|
||||
if (currentCountry == 'Syria') return 'لا حكم عليه'.tr;
|
||||
if (currentCountry == 'Egypt') return 'فيش وتشبيه'.tr;
|
||||
return 'عدم محكومية'.tr;
|
||||
}
|
||||
|
||||
/// التصرّف العام لاختيار/قص/ضغط/رفع الصورة حسب type
|
||||
Future<void> choosImage(String link, String imageType) async {
|
||||
try {
|
||||
@@ -329,58 +340,66 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
/// ترفع الملف وترجع رابط الصورة النهائي كـ String
|
||||
/// ترفع الملف وترجع رابط الصورة النهائي كـ String مع إعادة المحاولة في حال فشل الاتصال
|
||||
Future<String> uploadImage(
|
||||
File file, Map<String, String> data, String link) async {
|
||||
final uri = Uri.parse(link);
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
int maxRetries = 3;
|
||||
int attempt = 0;
|
||||
while (attempt < maxRetries) {
|
||||
attempt++;
|
||||
try {
|
||||
final uri = Uri.parse(link);
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
|
||||
// الهيدرز (كما عندك)
|
||||
final headers = <String, String>{
|
||||
'Authorization':
|
||||
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
};
|
||||
request.headers.addAll(headers);
|
||||
final headers = <String, String>{
|
||||
'Authorization':
|
||||
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
};
|
||||
request.headers.addAll(headers);
|
||||
|
||||
// اسم الملف: driverID.jpg (اختياري)
|
||||
final forcedName = '${box.read(BoxName.driverID) ?? 'image'}.jpg';
|
||||
final forcedName = '${box.read(BoxName.driverID) ?? 'image'}.jpg';
|
||||
|
||||
// إضافة الملف (من المسار مباشرة أسلم من الـ stream)
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'image', // تأكد أنه نفس اسم الحقل على السيرفر
|
||||
file.path,
|
||||
filename: forcedName,
|
||||
),
|
||||
);
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'image',
|
||||
file.path,
|
||||
filename: forcedName,
|
||||
),
|
||||
);
|
||||
|
||||
// الحقول الإضافية
|
||||
data.forEach((k, v) => request.fields[k] = v);
|
||||
data.forEach((k, v) => request.fields[k] = v);
|
||||
|
||||
// الإرسال
|
||||
final streamed = await request.send();
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
// المهلة الزمنية 120 ثانية لتناسب الاتصالات الضعيفة
|
||||
final streamed = await request.send().timeout(const Duration(seconds: 120));
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to upload image: ${res.statusCode} - ${res.body}');
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to upload image: ${res.statusCode} - ${res.body}');
|
||||
}
|
||||
|
||||
final body = jsonDecode(res.body);
|
||||
final String? url = body['url'] ??
|
||||
body['file_link'] ??
|
||||
body['image_url'] ??
|
||||
(body['data'] is Map ? body['data']['url'] : null);
|
||||
|
||||
if (url == null || url.isEmpty) {
|
||||
throw Exception(
|
||||
'Upload succeeded but no image URL found in response: ${res.body}');
|
||||
}
|
||||
|
||||
return url;
|
||||
} catch (e) {
|
||||
Log.print("⚠️ [Image Upload Attempt $attempt Failed] Error: $e");
|
||||
if (attempt >= maxRetries) {
|
||||
rethrow;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: attempt * 2));
|
||||
}
|
||||
}
|
||||
|
||||
// نحاول استخراج رابط الصورة من أكثر من مفتاح محتمل
|
||||
final body = jsonDecode(res.body);
|
||||
final String? url = body['url'] ??
|
||||
body['file_link'] ??
|
||||
body['image_url'] ??
|
||||
(body['data'] is Map ? body['data']['url'] : null);
|
||||
|
||||
if (url == null || url.isEmpty) {
|
||||
// لو السيرفر يرجع هيكل مختلف، عدّل هنا المفتاح حسب استجابتك الفعلية
|
||||
throw Exception(
|
||||
'Upload succeeded but no image URL found in response: ${res.body}');
|
||||
}
|
||||
|
||||
return url;
|
||||
throw Exception('Upload failed after $maxRetries attempts');
|
||||
}
|
||||
|
||||
Future<File> compressImage(File file) async {
|
||||
@@ -398,7 +417,7 @@ class RegistrationController extends GetxController {
|
||||
return File(result!.path);
|
||||
}
|
||||
|
||||
// دالة رفع إلى السيرفر السوري: ترجع file_url (Signed URL)
|
||||
// دالة رفع إلى السيرفر السوري مع مهلة 120 ثانية وإعادة محاولة تلقائية في حال الفشل
|
||||
Future<String> uploadToSyria({
|
||||
required String docType,
|
||||
required File file,
|
||||
@@ -406,79 +425,71 @@ class RegistrationController extends GetxController {
|
||||
required String authHeader,
|
||||
required String hmacHeader,
|
||||
required String driverId,
|
||||
Duration timeout = const Duration(seconds: 60),
|
||||
Duration timeout = const Duration(seconds: 120),
|
||||
http.Client? clientOverride,
|
||||
}) async {
|
||||
final client = clientOverride ?? http.Client();
|
||||
try {
|
||||
final mime = lookupMimeType(file.path) ?? 'image/jpeg';
|
||||
final parts = mime.split('/');
|
||||
|
||||
final req = http.MultipartRequest('POST', syrianUploadUri);
|
||||
req.headers.addAll({
|
||||
'Authorization': authHeader,
|
||||
'X-HMAC-Auth': hmacHeader,
|
||||
});
|
||||
|
||||
req.fields['driver_id'] = driverId;
|
||||
req.fields['doc_type'] = docType;
|
||||
|
||||
req.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'file',
|
||||
file.path,
|
||||
filename: p.basename(file.path),
|
||||
contentType: MediaType(parts.first, parts.last),
|
||||
),
|
||||
);
|
||||
|
||||
// ====== الطباعة قبل الإرسال ======
|
||||
// Log.print('--- Syrian Upload Request ---');
|
||||
// Log.print('URL: $syrianUploadUri');
|
||||
// // Log.print('Method: POST');
|
||||
// // Log.print('Headers: ${req.headers}');
|
||||
// Log.print('Fields: ${req.fields}');
|
||||
// // Log.print(
|
||||
// // 'File: ${file.path} (${await file.length()} bytes, mime: $mime)');
|
||||
// Log.print('-----------------------------');
|
||||
|
||||
// الإرسال
|
||||
final streamed = await client.send(req).timeout(timeout);
|
||||
final resp = await http.Response.fromStream(streamed);
|
||||
|
||||
// ====== الطباعة بعد الاستجابة ======
|
||||
// Log.print('--- Syrian Upload Response ---');
|
||||
Log.print('Status: ${resp.statusCode}');
|
||||
// Log.print('Headers: ${resp.headers}');
|
||||
// Log.print('Body: ${resp.body}');
|
||||
// Log.print('-------------------------------');
|
||||
|
||||
Map<String, dynamic> j = {};
|
||||
int maxRetries = 3;
|
||||
int attempt = 0;
|
||||
while (attempt < maxRetries) {
|
||||
attempt++;
|
||||
final client = clientOverride ?? http.Client();
|
||||
try {
|
||||
j = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||
final mime = lookupMimeType(file.path) ?? 'image/jpeg';
|
||||
final parts = mime.split('/');
|
||||
|
||||
final req = http.MultipartRequest('POST', syrianUploadUri);
|
||||
req.headers.addAll({
|
||||
'Authorization': authHeader,
|
||||
'X-HMAC-Auth': hmacHeader,
|
||||
});
|
||||
|
||||
req.fields['driver_id'] = driverId;
|
||||
req.fields['doc_type'] = docType;
|
||||
|
||||
req.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'file',
|
||||
file.path,
|
||||
filename: p.basename(file.path),
|
||||
contentType: MediaType(parts.first, parts.last),
|
||||
),
|
||||
);
|
||||
|
||||
final streamed = await client.send(req).timeout(timeout);
|
||||
final resp = await http.Response.fromStream(streamed);
|
||||
|
||||
Log.print('--- Syrian Upload Response (Attempt $attempt) ---');
|
||||
Log.print('Status: ${resp.statusCode}');
|
||||
|
||||
Map<String, dynamic> j = {};
|
||||
try {
|
||||
j = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
Log.print('⚠️ Failed to parse JSON: $e');
|
||||
}
|
||||
|
||||
final statusOk = j['status'] == 'success';
|
||||
final fileUrl = (j['file_url'] ?? j['message']?['file_url'])?.toString();
|
||||
|
||||
if (resp.statusCode == 200 &&
|
||||
statusOk &&
|
||||
(fileUrl?.isNotEmpty ?? false)) {
|
||||
return fileUrl!;
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'❌ Syrian upload failed ($docType): ${j['message'] ?? resp.body}');
|
||||
} catch (e) {
|
||||
Log.print('⚠️ Failed to parse JSON: $e');
|
||||
Log.print("⚠️ [Syria Upload Attempt $attempt Failed] Error: $e");
|
||||
if (attempt >= maxRetries) {
|
||||
rethrow;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: attempt * 2));
|
||||
} finally {
|
||||
if (clientOverride == null) client.close();
|
||||
}
|
||||
|
||||
// التحمّل لشكلين من الـ JSON:
|
||||
final statusOk = j['status'] == 'success';
|
||||
final fileUrl = (j['file_url'] ?? j['message']?['file_url'])?.toString();
|
||||
final fileName =
|
||||
(j['file_name'] ?? j['message']?['file_name'])?.toString();
|
||||
|
||||
if (resp.statusCode == 200 &&
|
||||
statusOk &&
|
||||
(fileUrl?.isNotEmpty ?? false)) {
|
||||
// Log.print(
|
||||
// '✅ Syrian upload success: $fileUrl (file: ${fileName ?? "-"})');
|
||||
return fileUrl!;
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'❌ Syrian upload failed ($docType): ${j['message'] ?? resp.body}');
|
||||
} finally {
|
||||
if (clientOverride == null) client.close();
|
||||
}
|
||||
throw Exception('Syrian upload failed after $maxRetries attempts');
|
||||
}
|
||||
|
||||
Future<void> submitRegistration() async {
|
||||
@@ -490,19 +501,24 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
|
||||
// 1) تحقق من وجود الروابط
|
||||
final driverFrontUrl = docUrls['driver_license_front'];
|
||||
final driverBackUrl = docUrls['driver_license_back'];
|
||||
final idFrontUrl = docUrls['id_front'];
|
||||
final idBackUrl = docUrls['id_back'];
|
||||
final driverLicenseUrl = docUrls['driver_license'];
|
||||
final driverLicenseBackUrl = docUrls['driver_license_back'];
|
||||
final profilePicUrl = docUrls['profile_picture'];
|
||||
final criminalRecordUrl = docUrls['criminal_record'];
|
||||
final carFrontUrl = docUrls['car_license_front'];
|
||||
final carBackUrl = docUrls['car_license_back'];
|
||||
Log.print(driverFrontUrl.toString());
|
||||
Log.print(driverBackUrl.toString());
|
||||
Log.print(carFrontUrl.toString());
|
||||
Log.print(carBackUrl.toString());
|
||||
|
||||
if (driverFrontUrl == null ||
|
||||
driverBackUrl == null ||
|
||||
carFrontUrl == null ||
|
||||
carBackUrl == null) {
|
||||
final isSyria = box.read(BoxName.countryCode) == 'Syria';
|
||||
|
||||
if (idFrontUrl == null || idFrontUrl.isEmpty ||
|
||||
idBackUrl == null || idBackUrl.isEmpty ||
|
||||
driverLicenseUrl == null || driverLicenseUrl.isEmpty ||
|
||||
(isSyria && (driverLicenseBackUrl == null || driverLicenseBackUrl.isEmpty)) ||
|
||||
profilePicUrl == null || profilePicUrl.isEmpty ||
|
||||
carFrontUrl == null || carFrontUrl.isEmpty ||
|
||||
carBackUrl == null || carBackUrl.isEmpty) {
|
||||
mySnackbarWarning('Please wait for all documents to finish uploading before registering.'.tr);
|
||||
return;
|
||||
}
|
||||
@@ -607,10 +623,14 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
|
||||
// --- روابط الصور ---
|
||||
_addField(fields, 'driver_license_front', driverFrontUrl!);
|
||||
_addField(fields, 'driver_license_back', driverBackUrl!);
|
||||
_addField(fields, 'car_license_front', carFrontUrl!);
|
||||
_addField(fields, 'car_license_back', carBackUrl!);
|
||||
_addField(fields, 'id_front', idFrontUrl);
|
||||
_addField(fields, 'id_back', idBackUrl);
|
||||
_addField(fields, 'driver_license', driverLicenseUrl);
|
||||
if (isSyria) _addField(fields, 'driver_license_back', driverLicenseBackUrl);
|
||||
_addField(fields, 'profile_picture', profilePicUrl);
|
||||
_addField(fields, 'criminal_record', criminalRecordUrl);
|
||||
_addField(fields, 'car_license_front', carFrontUrl);
|
||||
_addField(fields, 'car_license_back', carBackUrl);
|
||||
|
||||
req.fields.addAll(fields);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user