Initial push to my private server
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import '../../../constant/box_name.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
// --- Final Submission ---
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:path_provider/path_provider.dart' as path_provider;
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
@@ -21,8 +21,10 @@ import '../../../constant/colors.dart';
|
||||
import '../../../constant/info.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../../views/widgets/error_snakbar.dart';
|
||||
import '../../functions/crud.dart';
|
||||
import '../../functions/encrypt_decrypt.dart';
|
||||
import '../../functions/package_info.dart';
|
||||
import '../captin/login_captin_controller.dart';
|
||||
|
||||
// You can create a simple enum to manage image types
|
||||
@@ -41,6 +43,11 @@ class RegistrationController extends GetxController {
|
||||
|
||||
// Loading state
|
||||
var isLoading = false.obs;
|
||||
var isloading = false;
|
||||
CroppedFile? croppedFile;
|
||||
final picker = ImagePicker();
|
||||
var image;
|
||||
File? myImage;
|
||||
String? colorHex; // سيُملى من الدروب داون
|
||||
// Form Keys for validation
|
||||
final driverInfoFormKey = GlobalKey<FormState>();
|
||||
@@ -233,6 +240,142 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
/// خريطة لتخزين روابط المستندات بعد الرفع
|
||||
final Map<String, String> docUrls = {
|
||||
'driver_license_front': '',
|
||||
'driver_license_back': '',
|
||||
'car_license_front': '',
|
||||
'car_license_back': '',
|
||||
};
|
||||
|
||||
/// التصرّف العام لاختيار/قص/ضغط/رفع الصورة حسب type
|
||||
Future<void> choosImage(String link, String imageType) async {
|
||||
try {
|
||||
final pickedImage = await picker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
preferredCameraDevice: CameraDevice.rear,
|
||||
);
|
||||
if (pickedImage == null) return;
|
||||
|
||||
image = File(pickedImage.path);
|
||||
|
||||
final croppedFile = await ImageCropper().cropImage(
|
||||
sourcePath: image!.path,
|
||||
uiSettings: [
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: 'Cropper'.tr,
|
||||
toolbarColor: AppColor.blueColor,
|
||||
toolbarWidgetColor: AppColor.yellowColor,
|
||||
initAspectRatio: CropAspectRatioPreset.original,
|
||||
lockAspectRatio: false,
|
||||
),
|
||||
IOSUiSettings(title: 'Cropper'.tr),
|
||||
],
|
||||
);
|
||||
if (croppedFile == null) return;
|
||||
|
||||
// صورة للمعاينة داخل التطبيق
|
||||
myImage = File(croppedFile.path);
|
||||
|
||||
isloading = true;
|
||||
update();
|
||||
|
||||
// ضغط (وأيضاً يمكنك إضافة rotateImageIfNeeded قبل/بعد الضغط إن رغبت)
|
||||
final File compressedImage = await compressImage(File(croppedFile.path));
|
||||
|
||||
// تجهيز الحقول
|
||||
final driverId = box.read(BoxName.driverID);
|
||||
|
||||
final payload = <String, String>{
|
||||
'driverID': driverId,
|
||||
'imageType': imageType, // مثال: driver_license_front
|
||||
};
|
||||
|
||||
// الرفع وإرجاع الرابط
|
||||
final String imageUrl = await uploadImage(compressedImage, payload, link);
|
||||
|
||||
// حفظ الرابط محلياً حسب النوع
|
||||
docUrls[imageType] = imageUrl;
|
||||
|
||||
Log.print('✅ Uploaded $imageType => $imageUrl');
|
||||
} catch (e, st) {
|
||||
Log.print('❌ Error in choosImage: $e\n$st');
|
||||
mySnackeBarError('Image Upload Failed'.tr);
|
||||
} finally {
|
||||
isloading = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
/// ترفع الملف وترجع رابط الصورة النهائي كـ String
|
||||
Future<String> uploadImage(
|
||||
File file, Map<String, String> data, String link) async {
|
||||
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);
|
||||
|
||||
// اسم الملف: driverID.jpg (اختياري)
|
||||
final forcedName = '${box.read(BoxName.driverID) ?? 'image'}.jpg';
|
||||
|
||||
// إضافة الملف (من المسار مباشرة أسلم من الـ stream)
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'image', // تأكد أنه نفس اسم الحقل على السيرفر
|
||||
file.path,
|
||||
filename: forcedName,
|
||||
),
|
||||
);
|
||||
|
||||
// الحقول الإضافية
|
||||
data.forEach((k, v) => request.fields[k] = v);
|
||||
|
||||
// الإرسال
|
||||
final streamed = await request.send();
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Future<File> compressImage(File file) async {
|
||||
final dir = await path_provider.getTemporaryDirectory();
|
||||
final targetPath = "${dir.absolute.path}/temp.jpg";
|
||||
|
||||
var result = await FlutterImageCompress.compressAndGetFile(
|
||||
file.absolute.path,
|
||||
targetPath,
|
||||
quality: 70,
|
||||
minWidth: 1024,
|
||||
minHeight: 1024,
|
||||
);
|
||||
|
||||
return File(result!.path);
|
||||
}
|
||||
|
||||
// دالة رفع إلى السيرفر السوري: ترجع file_url (Signed URL)
|
||||
Future<String> uploadToSyria({
|
||||
required String docType,
|
||||
@@ -317,30 +460,17 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
|
||||
Future<void> submitRegistration() async {
|
||||
// 1) تحقق من الصور
|
||||
if (driverLicenseFrontImage == null ||
|
||||
driverLicenseBackImage == null ||
|
||||
carLicenseFrontImage == null ||
|
||||
carLicenseBackImage == null) {
|
||||
Get.snackbar(
|
||||
'Missing Documents'.tr,
|
||||
'Please upload all 4 required documents.'.tr,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.orange,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// 0) دوال/مساعدات محلية
|
||||
|
||||
// 1) تحقق من وجود الروابط بدل الملفات
|
||||
final driverFrontUrl = docUrls['driver_license_front'];
|
||||
final driverBackUrl = docUrls['driver_license_back'];
|
||||
final carFrontUrl = docUrls['car_license_front'];
|
||||
final carBackUrl = docUrls['car_license_back'];
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
// روابط الـ API
|
||||
final registerUri =
|
||||
Uri.parse(AppLink.register_driver_and_car); // التسجيل الرئيسي (PHP)
|
||||
final syrianUploadUri =
|
||||
// Uri.parse(AppLink.uploadSyrianDocs); // رفع الصور في سوريا
|
||||
Uri.parse(
|
||||
'https://syria.intaleq.xyz/intaleq/auth/syria/uploadSyrianDocs.php'); // رفع الصور في سوريا
|
||||
final registerUri = Uri.parse(AppLink.register_driver_and_car);
|
||||
|
||||
final client = http.Client();
|
||||
try {
|
||||
@@ -349,50 +479,7 @@ class RegistrationController extends GetxController {
|
||||
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}';
|
||||
final hmac = '${box.read(BoxName.hmac)}';
|
||||
|
||||
// 2) ارفع الصور أولاً على السيرفر السوري واحصل على روابطها (Signed URLs)
|
||||
final driverId = (box.read(BoxName.driverID) ?? '').toString();
|
||||
|
||||
final driverFrontUrl = await uploadToSyria(
|
||||
docType: 'driver_license_front',
|
||||
file: driverLicenseFrontImage!,
|
||||
syrianUploadUri: syrianUploadUri,
|
||||
authHeader: bearer,
|
||||
hmacHeader: hmac,
|
||||
driverId: driverId,
|
||||
clientOverride: client,
|
||||
);
|
||||
|
||||
final driverBackUrl = await uploadToSyria(
|
||||
docType: 'driver_license_back',
|
||||
file: driverLicenseBackImage!,
|
||||
syrianUploadUri: syrianUploadUri,
|
||||
authHeader: bearer,
|
||||
hmacHeader: hmac,
|
||||
driverId: driverId,
|
||||
clientOverride: client,
|
||||
);
|
||||
|
||||
final carFrontUrl = await uploadToSyria(
|
||||
docType: 'car_license_front',
|
||||
file: carLicenseFrontImage!,
|
||||
syrianUploadUri: syrianUploadUri,
|
||||
authHeader: bearer,
|
||||
hmacHeader: hmac,
|
||||
driverId: driverId,
|
||||
clientOverride: client,
|
||||
);
|
||||
|
||||
final carBackUrl = await uploadToSyria(
|
||||
docType: 'car_license_back',
|
||||
file: carLicenseBackImage!,
|
||||
syrianUploadUri: syrianUploadUri,
|
||||
authHeader: bearer,
|
||||
hmacHeader: hmac,
|
||||
driverId: driverId,
|
||||
clientOverride: client,
|
||||
);
|
||||
|
||||
// 3) جهّز طلب التسجيل الرئيسي: نرسل الحقول + روابط الصور (لا نرفع الصور مرة ثانية)
|
||||
// 2) جهّز طلب التسجيل الرئيسي: حقول فقط + روابط الصور (لا نرفع صور إطلاقًا)
|
||||
final req = http.MultipartRequest('POST', registerUri);
|
||||
req.headers.addAll({
|
||||
'Authorization': bearer,
|
||||
@@ -411,18 +498,16 @@ class RegistrationController extends GetxController {
|
||||
_addField(
|
||||
fields, 'password', 'generate_your_password_here'); // عدّل حسب منطقك
|
||||
_addField(fields, 'status', 'yet');
|
||||
_addField(fields, 'email',
|
||||
'Not specified'); // السيرفر سيحوّلها null ويبني ايميل افتراضي
|
||||
_addField(fields, 'email', 'Not specified');
|
||||
_addField(fields, 'gender', 'Male');
|
||||
|
||||
// --- Car Data ---
|
||||
_addField(fields, 'vin', 'yet'); // تم تصحيح الاقتباس
|
||||
_addField(fields, 'vin', 'yet');
|
||||
_addField(fields, 'car_plate', carPlateController.text);
|
||||
_addField(fields, 'make', carMakeController.text);
|
||||
_addField(fields, 'model', carModelController.text);
|
||||
_addField(fields, 'year', carYearController.text);
|
||||
_addField(fields, 'expiration_date',
|
||||
driverLicenseExpiryController.text); // تم التصحيح
|
||||
_addField(fields, 'expiration_date', driverLicenseExpiryController.text);
|
||||
_addField(fields, 'color', carColorController.text);
|
||||
_addField(fields, 'fuel', 'Gasoline');
|
||||
if (colorHex != null && colorHex!.isNotEmpty) {
|
||||
@@ -431,32 +516,32 @@ class RegistrationController extends GetxController {
|
||||
_addField(fields, 'owner',
|
||||
'${firstNameController.text} ${lastNameController.text}');
|
||||
|
||||
// --- روابط الصور الموقّعة من سوريا ---
|
||||
_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, 'driver_license_front', driverFrontUrl!);
|
||||
_addField(fields, 'driver_license_back', driverBackUrl!);
|
||||
_addField(fields, 'car_license_front', carFrontUrl!);
|
||||
_addField(fields, 'car_license_back', carBackUrl!);
|
||||
|
||||
// أضف الحقول
|
||||
req.fields.addAll(fields);
|
||||
|
||||
// 4) الإرسال
|
||||
// 3) الإرسال
|
||||
final streamed =
|
||||
await client.send(req).timeout(const Duration(seconds: 60));
|
||||
final resp = await http.Response.fromStream(streamed);
|
||||
|
||||
// 5) فحص النتيجة
|
||||
// 4) فحص النتيجة
|
||||
Map<String, dynamic>? json;
|
||||
try {
|
||||
json = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||
} catch (_) {}
|
||||
|
||||
if (resp.statusCode == 200 && json?['status'] == 'success') {
|
||||
final driverID =
|
||||
(json!['data']?['driverID'] ?? json['driverID'])?.toString();
|
||||
if (driverID != null && driverID.isNotEmpty) {
|
||||
box.write(BoxName.driverID, driverID);
|
||||
}
|
||||
// final driverID =
|
||||
// (json!['data']?['driverID'] ?? json['driverID'])?.toString();
|
||||
// if (driverID != null && driverID.isNotEmpty) {
|
||||
// box.write(BoxName.driverID, driverID);
|
||||
// }
|
||||
|
||||
Get.snackbar(
|
||||
'Success'.tr,
|
||||
@@ -466,20 +551,31 @@ class RegistrationController extends GetxController {
|
||||
colorText: Colors.white,
|
||||
);
|
||||
|
||||
// TODO: التنقّل أو تحديث الحالة…
|
||||
final email = box.read<String?>(BoxName.emailDriver) ?? '';
|
||||
|
||||
// متابعة تسجيل الدخول إن لزم
|
||||
final email = box.read(BoxName.emailDriver);
|
||||
final driverID = box.read(BoxName.driverID);
|
||||
final c = Get.isRegistered<LoginDriverController>()
|
||||
? Get.find<LoginDriverController>()
|
||||
: Get.put(LoginDriverController());
|
||||
//token to server
|
||||
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
|
||||
await CRUD().post(link: AppLink.addTokensDriver, payload: {
|
||||
'captain_id': (box.read(BoxName.driverID)).toString(),
|
||||
'token': (box.read(BoxName.tokenDriver)).toString(),
|
||||
'fingerPrint': fingerPrint.toString(),
|
||||
});
|
||||
await CRUD().post(link: AppLink.addTokensDriverWallet, payload: {
|
||||
'token': box.read(BoxName.tokenDriver).toString(),
|
||||
'fingerPrint': fingerPrint.toString(),
|
||||
'captain_id': box.read(BoxName.driverID).toString(),
|
||||
});
|
||||
|
||||
c.loginWithGoogleCredential(driverId, email);
|
||||
c.loginWithGoogleCredential(driverID, email);
|
||||
} else {
|
||||
final msg =
|
||||
(json?['message'] ?? 'Registration failed. Please try again.')
|
||||
.toString();
|
||||
Log.print('msg: ${msg}');
|
||||
|
||||
Log.print('msg: $msg');
|
||||
Get.snackbar(
|
||||
'Error'.tr,
|
||||
msg,
|
||||
@@ -500,8 +596,7 @@ class RegistrationController extends GetxController {
|
||||
client.close();
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
// Future<void> submitRegistration() async {
|
||||
} // Future<void> submitRegistration() async {
|
||||
// // 1) تحقق من الصور
|
||||
// if (driverLicenseFrontImage == null ||
|
||||
// driverLicenseBackImage == null ||
|
||||
|
||||
Reference in New Issue
Block a user