connect_error) { http_response_code(500); die(json_encode(['error' => 'Koneksi database gagal: ' . $conn->connect_error])); } $conn->set_charset('utf8mb4'); // ── Session ────────────────────────────────────────────── function startSession() { if (session_status() === PHP_SESSION_NONE) { if (!headers_sent()) { session_set_cookie_params(['httponly' => true, 'samesite' => 'Strict']); } session_start(); } } function requireLogin($roles = []) { startSession(); if (empty($_SESSION['user_id'])) { if (isApiRequest()) { http_response_code(401); echo json_encode(['error' => 'Tidak terautentikasi']); exit; } header('Location: login.php'); exit; } if (!empty($roles) && !in_array($_SESSION['role'], $roles)) { if (isApiRequest()) { http_response_code(403); echo json_encode(['error' => 'Akses ditolak']); exit; } header('Location: login.php?error=akses'); exit; } } function isApiRequest() { return strpos($_SERVER['REQUEST_URI'], '/api/') !== false || ($_SERVER['HTTP_ACCEPT'] ?? '') === 'application/json'; } function currentUser() { startSession(); return [ 'id' => $_SESSION['user_id'] ?? null, 'nama' => $_SESSION['nama'] ?? '', 'role' => $_SESSION['role'] ?? '', 'ibadah'=> $_SESSION['id_rumah_ibadah'] ?? null, ]; } // ── Haversine ──────────────────────────────────────────── function haversine($lat1, $lon1, $lat2, $lon2) { $R = 6371000; $phi1 = deg2rad($lat1); $phi2 = deg2rad($lat2); $dphi = deg2rad($lat2 - $lat1); $dl = deg2rad($lon2 - $lon1); $a = sin($dphi/2)**2 + cos($phi1)*cos($phi2)*sin($dl/2)**2; return $R * 2 * atan2(sqrt($a), sqrt(1-$a)); } function findNearestIbadah($conn, $lat, $lon) { $result = $conn->query("SELECT id, nama, latitude, longitude FROM rumah_ibadah"); $nearest = null; $minDist = PHP_INT_MAX; while ($row = $result->fetch_assoc()) { $dist = haversine($lat, $lon, $row['latitude'], $row['longitude']); if ($dist < $minDist) { $minDist = $dist; $nearest = $row; } } return $nearest ? ['id' => $nearest['id'], 'nama' => $nearest['nama'], 'jarak' => round($minDist, 2)] : null; } function findIbadahInRadius($conn, $lat, $lon) { // Cari rumah ibadah yang radius-nya mencakup koordinat ini $result = $conn->query("SELECT id, nama, radius, latitude, longitude FROM rumah_ibadah"); $matches = []; while ($row = $result->fetch_assoc()) { $dist = haversine($lat, $lon, $row['latitude'], $row['longitude']); if ($dist <= $row['radius']) $matches[] = ['id' => $row['id'], 'nama' => $row['nama'], 'jarak' => round($dist, 2)]; } return $matches ?: [findNearestIbadah($conn, $lat, $lon)]; } // ── Log aktivitas ───────────────────────────────────────── function logAktivitas($conn, $aksi, $tabel = null, $id = null, $desc = null) { startSession(); $uid = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : 'NULL'; $aksi = $conn->real_escape_string($aksi); $tbl = $conn->real_escape_string($tabel ?? ''); $desc = $conn->real_escape_string($desc ?? ''); $ip = $conn->real_escape_string($_SERVER['REMOTE_ADDR'] ?? ''); $id = $id ? (int)$id : 'NULL'; $conn->query("INSERT INTO log_aktivitas (id_user,aksi,tabel_terkait,id_terkait,deskripsi,ip_address) VALUES ($uid,'$aksi','$tbl',$id,'$desc','$ip')"); } // ── Notifikasi ──────────────────────────────────────────── function kirimNotifikasi($conn, $id_user, $judul, $isi, $tipe = 'info', $id_ref = null) { $judul = $conn->real_escape_string($judul); $isi = $conn->real_escape_string($isi); $tipe = $conn->real_escape_string($tipe); $id_ref = $id_ref ? (int)$id_ref : 'NULL'; $id_user = (int)$id_user; $conn->query("INSERT INTO notifikasi (id_user,judul,isi,tipe,id_referensi) VALUES ($id_user,'$judul','$isi','$tipe',$id_ref)"); } // ── Upload helper ───────────────────────────────────────── function uploadFile($fileKey, $prefix = 'file') { if (empty($_FILES[$fileKey]['name'])) return null; $dir = UPLOAD_DIR; if (!is_dir($dir)) mkdir($dir, 0755, true); $ext = strtolower(pathinfo($_FILES[$fileKey]['name'], PATHINFO_EXTENSION)); $allowed = ['jpg','jpeg','png','gif','pdf','webp']; if (!in_array($ext, $allowed)) return null; $name = $prefix . '_' . uniqid() . '.' . $ext; if (move_uploaded_file($_FILES[$fileKey]['tmp_name'], $dir . $name)) { return 'assets/uploads/' . $name; } return null; } // ── JSON response ───────────────────────────────────────── function jsonResponse($data, $code = 200) { http_response_code($code); header('Content-Type: application/json'); echo json_encode($data, JSON_UNESCAPED_UNICODE); exit; } // ── Escape shorthand ────────────────────────────────────── function esc($conn, $val) { return $conn->real_escape_string($val ?? ''); } ?>