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
@@ -0,0 +1,83 @@
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/camer_controller.dart';
import '../../../../controller/functions/ocr_controller.dart';
import '../../../widgets/my_scafold.dart';
class CameraLisencePage extends StatelessWidget {
CameraLisencePage.CameraLicensePage({super.key});
final CameraClassController cameraClassController =
Get.put(CameraClassController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Scan Driver License'.tr,
body: [
Column(children: [
Text(
'Please put your licence in these border'.tr,
style: AppStyle.title.copyWith(color: AppColor.greenColor),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
child: GetBuilder<CameraClassController>(
builder: (cameraClassController) =>
cameraClassController.isCameraInitialized
? Stack(
children: [
Container(
decoration: AppStyle.boxDecoration,
child: CameraPreview(
cameraClassController.cameraController,
),
),
Positioned(
top: Get.height * .1,
right: 5,
left: 5,
child: Container(
height: Get.width * 3 / 4,
width: Get.width * .9,
decoration: BoxDecoration(
// color: AppColor.blueColor,
border: Border.all(
color: AppColor.yellowColor, width: 2),
),
),
),
],
)
: Container(
decoration: AppStyle.boxDecoration,
height: Get.width * 3 / 4,
width: Get.width,
child: Center(
child: Text(
'Camera not initialized yet',
style: AppStyle.title,
),
),
),
),
),
const SizedBox(
height: 20,
),
MyElevatedButton(
title: 'Take Image'.tr,
onPressed: () {
ScanDocumentsByApi().scanDocumentsByApi();
},
)
]),
],
isleading: true,
);
}
}