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
+41
View File
@@ -0,0 +1,41 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/services.dart';
class Bubble {
static const MethodChannel _channel =
const MethodChannel('com.dsaved.bubble.head');
bool shouldBounce;
bool showCloseButton;
bool allowDragToClose;
Bubble({
this.shouldBounce = true,
this.allowDragToClose = true,
this.showCloseButton = false,
});
/// puts app in background and shows floaty-bubble head
Future<void> startBubbleHead({bool sendAppToBackground = true}) async {
ByteData bytes = await rootBundle.load(
'assets/images/s.png',
);
var buffer = bytes.buffer;
var encodedImage = base64.encode(Uint8List.view(buffer));
await _channel.invokeMethod('startBubbleHead', {
"image": encodedImage,
"bounce": shouldBounce,
"showClose": showCloseButton,
"dragToClose": allowDragToClose,
"sendAppToBackground": sendAppToBackground,
});
}
/// closes floaty-bubble head
Future<void> stopBubbleHead() async {
await _channel.invokeMethod('stopBubbleHead');
}
}