false, 'error' => 'penduduk_id diperlukan'], 400); } $stmt = $db->prepare(" SELECT f.*, u.nama AS nama_petugas FROM foto_rumah f LEFT JOIN users u ON u.id = f.diambil_oleh WHERE f.penduduk_id = ? ORDER BY f.is_primary DESC, f.created_at ASC "); $stmt->execute([$pendudukId]); header('Content-Type: application/json'); jsonResponse(['success' => true, 'data' => $stmt->fetchAll()]); break; // ════════════════════════════════════════════════════════ // POST – Upload foto baru // ════════════════════════════════════════════════════════ case 'POST': header('Content-Type: application/json'); $pendudukId = (int)($_POST['penduduk_id'] ?? 0); if (!$pendudukId) jsonResponse(['success' => false, 'error' => 'penduduk_id diperlukan'], 400); // Cek penduduk ada $cek = $db->prepare("SELECT id FROM penduduk_miskin WHERE id = ?"); $cek->execute([$pendudukId]); if (!$cek->fetch()) jsonResponse(['success' => false, 'error' => 'Data penduduk tidak ditemukan'], 404); if (!isset($_FILES['foto']) || $_FILES['foto']['error'] !== UPLOAD_ERR_OK) { $errCode = $_FILES['foto']['error'] ?? 'no_file'; jsonResponse(['success' => false, 'error' => "Upload gagal (kode: $errCode)"], 400); } $file = $_FILES['foto']; $tmpPath = $file['tmp_name']; $origName = $file['name']; $fileSize = $file['size']; // Validasi ukuran if ($fileSize > MAX_FOTO_SIZE) { jsonResponse(['success' => false, 'error' => 'Ukuran file maksimal 5 MB'], 413); } // Validasi ekstensi & MIME type $ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION)); $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $tmpPath); finfo_close($finfo); $allowedMime = ['image/jpeg','image/png','image/webp']; if (!in_array($ext, ALLOWED_EXT) || !in_array($mime, $allowedMime)) { jsonResponse(['success' => false, 'error' => 'Format file tidak didukung (jpg/png/webp)'], 415); } // Generate nama file unik $newName = 'foto_' . $pendudukId . '_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext; $destPath = UPLOAD_DIR . $newName; $relPath = UPLOAD_URL . $newName; if (!move_uploaded_file($tmpPath, $destPath)) { jsonResponse(['success' => false, 'error' => 'Gagal menyimpan file'], 500); } // Cek apakah ini foto pertama → jadikan primary $countStmt = $db->prepare("SELECT COUNT(*) FROM foto_rumah WHERE penduduk_id = ?"); $countStmt->execute([$pendudukId]); $isPrimary = ($countStmt->fetchColumn() == 0) ? 1 : 0; // Koordinat GPS dari POST (dari browser Geolocation atau EXIF) $latFoto = isset($_POST['lat_foto']) && $_POST['lat_foto'] !== '' ? (float)$_POST['lat_foto'] : null; $lngFoto = isset($_POST['lng_foto']) && $_POST['lng_foto'] !== '' ? (float)$_POST['lng_foto'] : null; $accuracy = isset($_POST['accuracy_meter']) && $_POST['accuracy_meter'] !== '' ? (float)$_POST['accuracy_meter'] : (isset($_POST['accuracy']) && $_POST['accuracy'] !== '' ? (float)$_POST['accuracy'] : null); // Jika user set is_primary = 1, nonaktifkan primary lain dulu $isPrimaryReq = isset($_POST['is_primary']) && (int)$_POST['is_primary'] === 1; if ($isPrimaryReq) { $db->prepare("UPDATE foto_rumah SET is_primary = 0 WHERE penduduk_id = ?") ->execute([$pendudukId]); $isPrimary = 1; } // diambil_oleh: support field name lama (user_id) dan baru (diambil_oleh) $diambilOleh = $_POST['diambil_oleh'] ?? $_POST['user_id'] ?? null; $db->prepare(" INSERT INTO foto_rumah (penduduk_id, file_path, lat_foto, lng_foto, accuracy_meter, is_primary, ukuran_kb, keterangan, diambil_oleh) VALUES (?,?,?,?,?,?,?,?,?) ")->execute([ $pendudukId, $relPath, $latFoto, $lngFoto, $accuracy, $isPrimary, (int)round($fileSize / 1024), $_POST['keterangan'] ?? null, $diambilOleh ? (int)$diambilOleh : null, ]); $newId = (int)$db->lastInsertId(); jsonResponse([ 'success' => true, 'id' => $newId, 'file_path' => $relPath, 'is_primary'=> $isPrimary, 'message' => 'Foto berhasil diupload', ]); break; // ════════════════════════════════════════════════════════ // DELETE – Hapus foto // ════════════════════════════════════════════════════════ case 'DELETE': header('Content-Type: application/json'); $id = (int)($_GET['id'] ?? 0); if (!$id) jsonResponse(['success' => false, 'error' => 'ID diperlukan'], 400); $stmt = $db->prepare("SELECT * FROM foto_rumah WHERE id = ?"); $stmt->execute([$id]); $foto = $stmt->fetch(); if (!$foto) jsonResponse(['success' => false, 'error' => 'Foto tidak ditemukan'], 404); // Hapus file fisik $filePath = __DIR__ . '/../' . $foto['file_path']; if (file_exists($filePath)) @unlink($filePath); $db->prepare("DELETE FROM foto_rumah WHERE id = ?")->execute([$id]); // Jika foto yang dihapus adalah primary, set foto lain jadi primary if ($foto['is_primary']) { $next = $db->prepare("SELECT id FROM foto_rumah WHERE penduduk_id = ? ORDER BY created_at ASC LIMIT 1"); $next->execute([$foto['penduduk_id']]); $nextFoto = $next->fetch(); if ($nextFoto) { $db->prepare("UPDATE foto_rumah SET is_primary = 1 WHERE id = ?")->execute([$nextFoto['id']]); } } jsonResponse(['success' => true, 'message' => 'Foto berhasil dihapus']); break; default: header('Content-Type: application/json'); jsonResponse(['success' => false, 'error' => 'Method tidak diizinkan'], 405); }