faceService = $faceService; } public function enrollFace(Request $request) { $validator = \Validator::make($request->all(), [ 'video_wajah' => 'required|file|mimetypes:video/mp4,video/quicktime,video/x-msvideo,video/3gpp,video/x-matroska|max:51200', ], [ 'video_wajah.required' => 'File video wajah wajib diupload.', 'video_wajah.file' => 'Data yang dikirim bukan file yang valid.', 'video_wajah.mimetypes' => 'Format video tidak didukung. Gunakan MP4, MOV, atau AVI.', 'video_wajah.max' => 'Ukuran video terlalu besar (maksimal 50MB).', ]); if ($validator->fails()) { Log::warning('Face Enrollment Validation Failed', [ 'errors' => $validator->errors()->toArray(), 'has_file' => $request->hasFile('video_wajah'), 'file_size' => $request->hasFile('video_wajah') ? $request->file('video_wajah')->getSize() : null, 'file_mime' => $request->hasFile('video_wajah') ? $request->file('video_wajah')->getMimeType() : null, ]); return response()->json([ 'success' => false, 'message' => $validator->errors()->first(), 'errors' => $validator->errors() ], 422); } try { $user = $request->user(); $video = $request->file('video_wajah'); $result = $this->faceService->enrollFace($user->id, $video); return response()->json([ 'success' => true, 'message' => $result['message'], ], 200); } catch (\Exception $e) { Log::error('Face Enrollment Error: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'Terjadi kesalahan sistem: ' . $e->getMessage() ], 500); } } public function getFaceStatus(Request $request) { $user = $request->user(); $dataWajah = \DB::table('data_wajah')->where('id_user', $user->id)->first(); $status = 'not_registered'; if ($user->is_face_registered) { $status = 'pending'; if ($dataWajah && $dataWajah->is_verified == StatusVerifikasiWajah::APPROVED) { $status = 'verified'; } elseif ($dataWajah && $dataWajah->is_verified == StatusVerifikasiWajah::REJECTED) { $status = 'rejected'; } } return response()->json([ 'success' => true, 'data' => [ 'status' => $status, 'is_registered' => (bool) $user->is_face_registered ] ]); } public function verifyFace(Request $request) { $request->validate([ 'file_wajah' => 'required|file|max:20480', 'tipe' => 'nullable|string|in:presensi,test', ]); $tipe = $request->input('tipe', 'presensi'); try { set_time_limit(0); $user = $request->user(); $file = $request->file('file_wajah'); $result = $this->faceService->verifyFace($user->id, $file); $predictedUserId = $result['predicted_user'] ?? null; $predictedName = null; if ($predictedUserId && is_numeric($predictedUserId)) { $predictedUser = \DB::table('users') ->where('id', $predictedUserId) ->value('nama_lengkap'); $predictedName = $predictedUser ?? "User #{$predictedUserId}"; } elseif ($predictedUserId === 'unknown') { $predictedName = 'Tidak Dikenal'; } $skorSvm = isset($result['svm_df']) ? round(max(-9999.9999, min(9999.9999, (float) $result['svm_df'])), 4) : null; $skorKepercayaan = isset($result['confidence']) ? round(max(-9999.9999, min(9999.9999, (float) $result['confidence'])), 4) : null; \DB::table('log_verifikasi_wajah')->insert([ 'id_user' => $user->id, 'skor_kepercayaan' => $skorKepercayaan, 'skor_svm' => $skorSvm, 'jarak_normalisasi'=> null, 'status_verifikasi'=> $result['verification_status'] ?? null, 'apakah_cocok' => $result['verified'] ?? false, 'skor_blur' => $result['blur_score'] ?? null, 'tipe' => $tipe, 'created_at' => now(), 'updated_at' => now(), ]); return response()->json([ 'success' => true, 'message' => 'Wajah terverifikasi', 'data' => array_merge($result, [ 'predicted_name' => $predictedName, ]), ]); } catch (\Exception $e) { if (isset($user)) { \DB::table('log_verifikasi_wajah')->insert([ 'id_user' => $user->id, 'skor_kepercayaan' => 0, 'status_verifikasi' => 'ERROR', 'apakah_cocok' => false, 'tipe' => $tipe ?? 'presensi', 'created_at' => now(), 'updated_at' => now(), ]); } return response()->json([ 'success' => false, 'message' => 'Verifikasi Gagal: ' . $e->getMessage() ], 400); } } }