request->getJSON(true) ?? []; $studentId = (int) ($payload['student_id'] ?? 0); $imageData = (string) ($payload['image'] ?? ''); if ($studentId < 1) { return $this->errorResponse('student_id wajib diisi', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY); } if ($imageData === '') { return $this->errorResponse('image (base64) wajib diisi', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY); } $tmpFile = tempnam(sys_get_temp_dir(), 'probe_'); try { $raw = $this->decodeBase64Image($imageData); if ($raw === null) { throw new \RuntimeException('Format gambar tidak valid (harus base64)'); } file_put_contents($tmpFile, $raw); $faceService = new FaceService(); $probe = $faceService->extractEmbeddingWithQuality($tmpFile)['embedding']; $faceModel = new StudentFaceModel(); $rows = $faceModel->where('student_id', $studentId)->findAll(); if (empty($rows)) { return $this->errorResponse('Belum ada embedding wajah untuk siswa ini', null, null, ResponseInterface::HTTP_NOT_FOUND); } $bestSim = -1.0; $bestSource = null; foreach ($rows as $row) { $embedding = json_decode($row['embedding'] ?? '[]', true); if (! is_array($embedding) || $embedding === []) { continue; } $sim = $faceService->cosineSimilarity($probe, array_map('floatval', $embedding)); if ($sim > $bestSim) { $bestSim = $sim; $bestSource = $row['source'] ?? null; } } if ($bestSim < 0) { return $this->errorResponse('Tidak ada embedding valid untuk siswa ini', null, null, ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } $threshold = $faceService->getDefaultThreshold(); $status = $bestSim >= $threshold ? 'match' : 'no_match'; $data = [ 'student_id' => $studentId, 'similarity' => $bestSim, 'threshold' => $threshold, 'matched_source'=> $bestSource, 'status' => $status, ]; return $this->successResponse($data, 'Face verification processed'); } catch (\Throwable $e) { return $this->errorResponse('Gagal verifikasi wajah: ' . $e->getMessage(), null, null, ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } finally { if (is_file($tmpFile)) { @unlink($tmpFile); } } } protected function decodeBase64Image(string $input): ?string { $input = trim($input); if ($input === '') { return null; } if (strpos($input, 'base64,') !== false) { $parts = explode('base64,', $input, 2); $input = $parts[1]; } $data = base64_decode($input, true); return $data === false ? null : $data; } }