TKK_E32230273/save_foto.php

164 lines
5.0 KiB
PHP

<?php
include 'koneksi.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(["status" => "error", "message" => "Method tidak diizinkan"]);
exit;
}
// Validasi input foto
if (!isset($_POST['foto'])) {
echo json_encode(["status" => "error", "message" => "Data foto tidak ada"]);
exit;
}
$fotoData = $_POST['foto'];
$id_occupant = null;
$token = null;
// Method 1: Token-based (from QR code scan)
if (isset($_POST['token']) && trim($_POST['token']) !== '') {
$token = trim($_POST['token']);
// Validate token
$stmt = $pdo->prepare("SELECT ft.id, ft.id_occupant, ft.status FROM foto_tokens ft WHERE ft.token = ?");
$stmt->execute([$token]);
$tokenRow = $stmt->fetch();
if (!$tokenRow) {
echo json_encode(["status" => "error", "message" => "Token tidak valid"]);
exit;
}
if ($tokenRow['status'] === 'used') {
echo json_encode(["status" => "error", "message" => "Token sudah digunakan (kadaluwarsa)"]);
exit;
}
$id_occupant = $tokenRow['id_occupant'];
}
// Method 2: Direct id_occupant (backward compat)
elseif (isset($_POST['id_occupant']) && trim($_POST['id_occupant']) !== '') {
$id_occupant = trim($_POST['id_occupant']);
}
else {
echo json_encode(["status" => "error", "message" => "Token atau ID Occupant diperlukan"]);
exit;
}
// Validasi id_occupant ada di database
$cek = $pdo->prepare("SELECT id_occupant, foto FROM occupant WHERE id_occupant = ?");
$cek->execute([$id_occupant]);
$occupant = $cek->fetch();
if (!$occupant) {
echo json_encode(["status" => "error", "message" => "Occupant tidak ditemukan"]);
exit;
}
// Decode base64 image
if (preg_match('/^data:image\/(png|jpeg|jpg|webp);base64,/', $fotoData, $matches)) {
$ext = $matches[1] === 'jpeg' ? 'jpg' : $matches[1];
$fotoData = preg_replace('/^data:image\/\w+;base64,/', '', $fotoData);
$fotoData = base64_decode($fotoData);
if ($fotoData === false) {
echo json_encode(["status" => "error", "message" => "Gagal decode gambar"]);
exit;
}
// Validate image size (min 50KB, max 10MB)
$imgSize = strlen($fotoData);
if ($imgSize < 50000) {
echo json_encode(["status" => "error", "message" => "Ukuran gambar terlalu kecil (min 50KB)"]);
exit;
}
if ($imgSize > 10485760) {
echo json_encode(["status" => "error", "message" => "Ukuran gambar terlalu besar (max 10MB)"]);
exit;
}
} else {
echo json_encode(["status" => "error", "message" => "Format gambar tidak valid"]);
exit;
}
// Buat folder jika belum ada
$uploadDir = __DIR__ . '/images/foto/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Hapus foto lama jika ada
if (!empty($occupant['foto'])) {
$oldPath = __DIR__ . '/' . $occupant['foto'];
if (file_exists($oldPath)) {
unlink($oldPath);
}
}
// Simpan foto baru
$fotoName = 'foto_' . $id_occupant . '_' . time() . '.' . $ext;
$targetPath = $uploadDir . $fotoName;
if (file_put_contents($targetPath, $fotoData) === false) {
echo json_encode(["status" => "error", "message" => "Gagal menyimpan foto"]);
exit;
}
// Save capture metadata for audit trail
$metadata = [
'liveness_score' => isset($_POST['liveness_score']) ? intval($_POST['liveness_score']) : null,
'capture_time' => isset($_POST['capture_time']) ? $_POST['capture_time'] : null,
'device_info' => isset($_POST['device_info']) ? substr($_POST['device_info'], 0, 500) : null,
'screen_res' => isset($_POST['screen_res']) ? $_POST['screen_res'] : null,
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
'saved_at' => date('Y-m-d H:i:s'),
];
$metaPath = $uploadDir . 'meta_' . $id_occupant . '_' . time() . '.json';
file_put_contents($metaPath, json_encode($metadata, JSON_PRETTY_PRINT));
$fotoRelPath = 'images/foto/' . $fotoName;
// Update kolom foto di tabel occupant + mark token as used
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("UPDATE occupant SET foto = ? WHERE id_occupant = ?");
$stmt->execute([$fotoRelPath, $id_occupant]);
// Mark token as used if token-based
if ($token) {
$pdo->prepare("UPDATE foto_tokens SET status = 'used', used_at = NOW() WHERE token = ?")
->execute([$token]);
}
$pdo->commit();
// Update state untuk ESP32 (memberi sinyal polling)
$stateFile = __DIR__ . '/esp32_state.json';
if (file_exists($stateFile)) {
$stateData = json_decode(file_get_contents($stateFile), true);
if (is_array($stateData)) {
$stateData['photo_done'] = true;
file_put_contents($stateFile, json_encode($stateData));
}
}
echo json_encode([
"status" => "success",
"message" => "Foto berhasil disimpan",
"foto" => $fotoRelPath
]);
} catch (PDOException $e) {
$pdo->rollBack();
if (file_exists($targetPath)) {
unlink($targetPath);
}
echo json_encode([
"status" => "error",
"message" => "Gagal update database: " . $e->getMessage()
]);
}
?>