import 'dart:io'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart'; import '../core/constants/api_url.dart'; import '../core/cache_manager.dart'; class FaceRepository { Future> enrollFace({ required File videoFile, }) async { final url = '${ApiUrl.baseUrl}/face/enroll'; final fileSize = await videoFile.length(); final fileSizeMB = (fileSize / 1024 / 1024).toStringAsFixed(2); print('[FaceRepo] URL: $url'); print('[FaceRepo] File: ${videoFile.path}, Size: $fileSizeMB MB'); try { final token = CacheManager.authBox.get('auth_token'); print('[FaceRepo] Token: ${token != null ? "ada (${token.toString().substring(0, 20)}...)" : "NULL!"}'); var request = http.MultipartRequest('POST', Uri.parse(url)); request.headers.addAll({ 'Authorization': 'Bearer $token', 'Accept': 'application/json', }); request.files.add(await http.MultipartFile.fromPath( 'video_wajah', videoFile.path, contentType: MediaType('video', 'mp4'), )); print('[FaceRepo] Sending request...'); final stopwatch = Stopwatch()..start(); var streamedResponse = await request.send().timeout( const Duration(minutes: 5), ); stopwatch.stop(); print('[FaceRepo] Response received in ${stopwatch.elapsed.inSeconds}s, status: ${streamedResponse.statusCode}'); var response = await http.Response.fromStream(streamedResponse); print('[FaceRepo] Body: ${response.body}'); Map body; try { body = jsonDecode(response.body); } catch (_) { throw Exception('Server mengembalikan format tidak valid. Coba ulangi lagi.'); } if (response.statusCode == 200 || response.statusCode == 201) { return { 'status': true, 'message': body['message'] ?? 'Pendaftaran wajah berhasil', 'data': body['data'], }; } else { throw Exception(body['message'] ?? 'Gagal mendaftarkan wajah: ${response.statusCode}'); } } catch (e) { print('[FaceRepo] ERROR: $e'); rethrow; } } Future> verifyFace(File videoFile, {String tipe = 'presensi'}) async { final token = CacheManager.authBox.get('auth_token'); var request = http.MultipartRequest( 'POST', Uri.parse('${ApiUrl.baseUrl}/face/verify'), ); request.headers.addAll({ 'Authorization': 'Bearer $token', 'Accept': 'application/json', }); request.files.add(await http.MultipartFile.fromPath( 'file_wajah', videoFile.path, contentType: MediaType('video', 'mp4'), )); request.fields['tipe'] = tipe; var streamedResponse = await request.send().timeout( const Duration(minutes: 3), ); var response = await http.Response.fromStream(streamedResponse); final body = jsonDecode(response.body); if (response.statusCode == 200 && body['success'] == true) { return body['data']; } throw Exception(body['message'] ?? 'Verifikasi gagal'); } Future> getFaceStatus() async { try { final token = CacheManager.authBox.get('auth_token'); final response = await http.get( Uri.parse('${ApiUrl.baseUrl}/face/status'), headers: { 'Authorization': 'Bearer $token', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { final data = jsonDecode(response.body); return data['data']; } throw Exception('Gagal mengambil status: ${response.statusCode}'); } catch (e) { rethrow; } } }