feat: Gemini AI (doc OCR + face match) + role-assignment endpoints

- GeminiService (integrations/gemini, @Global): extractDocument (OCR fields) + faceMatch; graceful if no GEMINI_API_KEY
- documents: face-match now real via Gemini; POST /admin/documents/:id/ocr extracts fields for CS auto-fill
- roles: PATCH /admin/users/:id/role (admin) + POST /platform/users/role (bootstrap via x-platform-secret)
- config: gemini + platform.secret; .env.example entries

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 20:01:26 +03:00
co-authored by Claude Opus 4.8
parent 04200db438
commit 6234e66471
1541 changed files with 267713 additions and 16 deletions
+94
View File
@@ -0,0 +1,94 @@
import 'dart:convert';
import 'dart:io';
import 'package:sefer_driver/constant/api_key.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/main.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart';
import '../../constant/links.dart';
import 'encrypt_decrypt.dart';
import 'upload_image.dart';
Future<String> faceDetector() async {
await ImageController().choosFace(AppLink.uploadEgypt, 'face_detect');
await Future.delayed(const Duration(seconds: 2));
var headers = {
// 'Authorization': 'Basic ${AK.basicCompareFaces}',
'Authorization': 'Basic hamza:12345678',
'Content-Type': 'application/json'
};
var request = http.Request(
// todo change server link
'POST',
Uri.parse('https://mohkh.online:5000/compare_faces'));
request.body = json.encode({
"url1": // TODO: jpg?
"${AppLink.server}/auth/uploads/documents_egypt/eg_id_front_${(box.read(BoxName.driverID))}.webp",
"url2":
"${AppLink.server}/card_image/face_detect-${(box.read(BoxName.driverID))}.jpg"
});
print('request.body: ${request.body}');
request.headers.addAll(headers);
try {
http.Client client = await createHttpClient();
http.StreamedResponse response = await client.send(request);
// http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
String result = await response.stream.bytesToString();
print('result: ${result}');
return result;
} else {
print('Error: ${response.reasonPhrase}');
return 'Error: ${response.reasonPhrase}';
}
} catch (e) {
print('Exception occurred: $e');
return 'Error: $e';
}
}
Future<http.Client> createHttpClient() async {
final SecurityContext securityContext = SecurityContext();
HttpClient httpClient = HttpClient(context: securityContext);
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) => true; // Bypass SSL
return IOClient(httpClient);
}
Future<String> faceDetector2(String url1, String url2) async {
var headers = {
'Authorization': 'Basic hamza:12345678',
'Content-Type': 'application/json'
};
var request = http.Request(
'POST', Uri.parse('https://mohkh.online:5000/compare_faces'));
request.body = json.encode({"url1": url1, "url2": url2});
request.headers.addAll(headers);
try {
http.Client client = await createHttpClient(); // Use custom client
DateTime startTime = DateTime.now();
http.StreamedResponse response = await client.send(request);
DateTime endTime = DateTime.now();
Duration duration = endTime.difference(startTime);
if (response.statusCode == 200) {
print(await response.stream.bytesToString());
print(duration.inSeconds);
return await response.stream.bytesToString();
} else {
print(await response.stream.bytesToString());
return 'Error: ${response.reasonPhrase}';
}
} catch (e) {
return 'Exception: $e';
}
}