Deploy WebGIS 01-05 dengan Docker

This commit is contained in:
2026-06-12 17:58:06 +07:00
commit 1bf000979c
76 changed files with 12301 additions and 0 deletions
+314
View File
@@ -0,0 +1,314 @@
<?php
/**
* auth.php
* Fungsi session, role pengguna, response JSON, validasi umum,
* prepared statement helper, perhitungan Haversine, dan sinkronisasi area.
*/
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once __DIR__ . '/koneksi.php';
function json_response($status = 'ok', $message = '', $data = [], $http_code = 200) {
http_response_code($http_code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'status' => $status,
'message' => $message,
'data' => $data
], JSON_UNESCAPED_UNICODE);
exit;
}
function require_post() {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
json_response('err', 'Method harus POST.', [], 405);
}
}
function is_logged_in() {
return isset($_SESSION['user_id'], $_SESSION['role']);
}
function require_login($json = false) {
if (!is_logged_in()) {
if ($json) json_response('err', 'Silakan login terlebih dahulu.', [], 401);
header('Location: login.php');
exit;
}
}
function current_user() {
global $koneksi;
if (!is_logged_in()) return null;
$id = (int) $_SESSION['user_id'];
$stmt = $koneksi->prepare("SELECT id, nama, username, role, status_akun FROM users WHERE id=? LIMIT 1");
$stmt->bind_param('i', $id);
$stmt->execute();
$res = $stmt->get_result();
return $res->fetch_assoc();
}
function has_role($roles) {
if (!is_array($roles)) $roles = [$roles];
return is_logged_in() && in_array($_SESSION['role'], $roles, true);
}
function require_role($roles, $json = false) {
if (!has_role($roles)) {
if ($json) json_response('err', 'Akses ditolak. Role tidak sesuai.', [], 403);
http_response_code(403);
echo '<h2>403 - Akses ditolak</h2>';
exit;
}
}
function post($key, $default = '') {
return isset($_POST[$key]) ? trim((string) $_POST[$key]) : $default;
}
function getv($key, $default = '') {
return isset($_GET[$key]) ? trim((string) $_GET[$key]) : $default;
}
function clean_text($str, $max = 255) {
$str = trim((string) $str);
if (mb_strlen($str) > $max) $str = mb_substr($str, 0, $max);
return $str;
}
function valid_enum($value, $allowed) {
return in_array($value, $allowed, true);
}
function valid_latlng($lat, $lng) {
return is_numeric($lat) && is_numeric($lng) && $lat >= -90 && $lat <= 90 && $lng >= -180 && $lng <= 180;
}
function haversine_meter($lat1, $lon1, $lat2, $lon2) {
$R = 6371000; // meter
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat / 2) * sin($dLat / 2) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon / 2) * sin($dLon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return $R * $c;
}
function stmt_bind($stmt, $types, $params) {
if ($types === '' || empty($params)) return true;
$refs = [];
$refs[] = $types;
foreach ($params as $k => $v) {
$refs[] = &$params[$k];
}
return call_user_func_array([$stmt, 'bind_param'], $refs);
}
function fetch_all_stmt($stmt) {
$stmt->execute();
$result = $stmt->get_result();
$rows = [];
while ($row = $result->fetch_assoc()) $rows[] = $row;
return $rows;
}
function build_in_condition($column, $values, &$types, &$params) {
$ids = [];
foreach ((array)$values as $v) {
$iv = (int)$v;
if ($iv > 0) $ids[] = $iv;
}
$ids = array_values(array_unique($ids));
if (empty($ids)) return '1=0';
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$types .= str_repeat('i', count($ids));
foreach ($ids as $id) $params[] = $id;
return "$column IN ($placeholders)";
}
function get_admin_rumah_ibadah_list($user_id = null, $only_diterima = true) {
global $koneksi;
if ($user_id === null) $user_id = (int) $_SESSION['user_id'];
$sql = "SELECT * FROM rumah_ibadah WHERE user_id=? AND COALESCE(status_hapus,'normal')<>'disetujui'";
if ($only_diterima) $sql .= " AND status_pengajuan='diterima'";
$sql .= " ORDER BY id ASC";
$stmt = $koneksi->prepare($sql);
$stmt->bind_param('i', $user_id);
return fetch_all_stmt($stmt);
}
function get_admin_rumah_ibadah_ids($user_id = null, $only_diterima = true) {
$rows = get_admin_rumah_ibadah_list($user_id, $only_diterima);
return array_map('intval', array_column($rows, 'id'));
}
function get_admin_rumah_ibadah($user_id = null) {
// Dipertahankan untuk kompatibilitas lama. Untuk logic baru, pakai get_admin_rumah_ibadah_list()/ids().
$rows = get_admin_rumah_ibadah_list($user_id, true);
return $rows[0] ?? null;
}
function is_admin_rumah_ibadah_id($rumah_ibadah_id, $user_id = null, $only_diterima = true) {
if ($user_id === null) $user_id = (int) $_SESSION['user_id'];
$ids = get_admin_rumah_ibadah_ids($user_id, $only_diterima);
return in_array((int)$rumah_ibadah_id, $ids, true);
}
function get_nearest_rumah_ibadah($lat, $lng, $max_meter = 300, $admin_user_id = null) {
global $koneksi;
$rows = [];
if ($admin_user_id !== null) {
$stmt = $koneksi->prepare("SELECT * FROM rumah_ibadah WHERE user_id=? AND status_pengajuan='diterima' AND COALESCE(status_hapus,'normal')<>'disetujui'");
$admin_user_id = (int)$admin_user_id;
$stmt->bind_param('i', $admin_user_id);
$rumahRows = fetch_all_stmt($stmt);
} else {
$rumahRows = [];
$res = $koneksi->query("SELECT * FROM rumah_ibadah WHERE status_pengajuan='diterima' AND COALESCE(status_hapus,'normal')<>'disetujui'");
while ($row = $res->fetch_assoc()) $rumahRows[] = $row;
}
foreach ($rumahRows as $row) {
$jarak = haversine_meter((float) $lat, (float) $lng, (float) $row['latitude'], (float) $row['longitude']);
if ($jarak <= $max_meter) {
$row['jarak_meter'] = round($jarak, 2);
$rows[] = $row;
}
}
usort($rows, fn($a, $b) => $a['jarak_meter'] <=> $b['jarak_meter']);
return $rows[0] ?? null;
}
function get_penerima_by_id($id) {
global $koneksi;
$stmt = $koneksi->prepare("SELECT pb.*, ri.nama_rumah_ibadah, ri.user_id AS admin_rumah_user_id, ri.latitude AS ri_latitude, ri.longitude AS ri_longitude FROM penerima_bantuan pb LEFT JOIN rumah_ibadah ri ON ri.id=pb.rumah_ibadah_id WHERE pb.id=? LIMIT 1");
$stmt->bind_param('i', $id);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
function get_rumah_ibadah_by_id($id) {
global $koneksi;
$stmt = $koneksi->prepare("SELECT ri.*, u.nama AS nama_admin, u.username AS username_admin FROM rumah_ibadah ri LEFT JOIN users u ON u.id=ri.user_id WHERE ri.id=? LIMIT 1");
$stmt->bind_param('i', $id);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
function can_manage_rumah_ibadah($rumah) {
if (!$rumah || !is_logged_in()) return false;
if ($_SESSION['role'] === 'super_admin') return true;
if ($_SESSION['role'] === 'admin_rumah_ibadah') {
return (int)$rumah['user_id'] === (int)$_SESSION['user_id'];
}
return false;
}
function can_manage_penerima($penerima) {
if (!$penerima || !is_logged_in()) return false;
if ($_SESSION['role'] === 'super_admin') return true;
if ($_SESSION['role'] === 'penerima' && (int) $penerima['user_id'] === (int) $_SESSION['user_id']) return true;
if ($_SESSION['role'] === 'admin_rumah_ibadah') {
return !empty($penerima['rumah_ibadah_id']) && is_admin_rumah_ibadah_id((int)$penerima['rumah_ibadah_id'], null, true);
}
return false;
}
function insert_riwayat_status($penerima_id, $status_lama, $status_baru, $catatan) {
global $koneksi;
$uid = (int) $_SESSION['user_id'];
$stmt = $koneksi->prepare("INSERT INTO riwayat_status (penerima_id, user_id, status_lama, status_baru, catatan) VALUES (?,?,?,?,?)");
$stmt->bind_param('iisss', $penerima_id, $uid, $status_lama, $status_baru, $catatan);
$stmt->execute();
}
function get_user_by_username_role($username, $roles) {
global $koneksi;
if (!is_array($roles)) $roles = [$roles];
$username = clean_text($username, 50);
if ($username === '') return null;
$placeholders = implode(',', array_fill(0, count($roles), '?'));
$types = 's' . str_repeat('s', count($roles));
$params = array_merge([$username], $roles);
$stmt = $koneksi->prepare("SELECT id, nama, username, role, status_akun FROM users WHERE username=? AND role IN ($placeholders) AND status_akun='aktif' LIMIT 1");
stmt_bind($stmt, $types, $params);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
function has_active_penerima_pin($user_id, $exclude_penerima_id = 0) {
global $koneksi;
$user_id = (int)$user_id;
$exclude_penerima_id = (int)$exclude_penerima_id;
if ($user_id <= 0) return false;
$sql = "SELECT id FROM penerima_bantuan
WHERE user_id=?
AND status_pengajuan IN ('menunggu','diterima')
AND COALESCE(status_hapus,'normal')<>'disetujui'";
$types = 'i';
$params = [$user_id];
if ($exclude_penerima_id > 0) {
$sql .= " AND id<>?";
$types .= 'i';
$params[] = $exclude_penerima_id;
}
$sql .= " LIMIT 1";
$stmt = $koneksi->prepare($sql);
stmt_bind($stmt, $types, $params);
$stmt->execute();
return (bool)$stmt->get_result()->fetch_assoc();
}
function sync_single_penerima_area($penerima_id) {
global $koneksi;
$penerima_id = (int)$penerima_id;
$stmt = $koneksi->prepare("SELECT id, latitude, longitude FROM penerima_bantuan WHERE id=? AND status_pengajuan IN ('menunggu','diterima') AND COALESCE(status_hapus,'normal')<>'disetujui' LIMIT 1");
$stmt->bind_param('i', $penerima_id);
$stmt->execute();
$pb = $stmt->get_result()->fetch_assoc();
if (!$pb) return false;
$nearest = get_nearest_rumah_ibadah((float)$pb['latitude'], (float)$pb['longitude'], 300);
if ($nearest) {
$riId = (int)$nearest['id'];
$jarak = (float)$nearest['jarak_meter'];
$stmtUp = $koneksi->prepare("UPDATE penerima_bantuan SET rumah_ibadah_id=?, jarak_meter=? WHERE id=?");
$stmtUp->bind_param('idi', $riId, $jarak, $penerima_id);
} else {
$stmtUp = $koneksi->prepare("UPDATE penerima_bantuan SET rumah_ibadah_id=NULL, jarak_meter=NULL WHERE id=?");
$stmtUp->bind_param('i', $penerima_id);
}
return $stmtUp->execute();
}
function sync_all_penerima_area() {
global $koneksi;
$ids = [];
$res = $koneksi->query("SELECT id FROM penerima_bantuan WHERE status_pengajuan IN ('menunggu','diterima') AND COALESCE(status_hapus,'normal')<>'disetujui'");
while ($row = $res->fetch_assoc()) $ids[] = (int)$row['id'];
$updated = 0;
foreach ($ids as $id) {
if (sync_single_penerima_area($id)) $updated++;
}
return $updated;
}
function sync_penerima_area_by_rumah_ibadah($rumah_ibadah_id) {
// Tetap menerima parameter lama, tetapi sinkronisasi dihitung ulang secara global agar radius beririsan memilih RI terdekat.
return sync_all_penerima_area();
}
function detach_penerima_from_rumah_ibadah($rumah_ibadah_id) {
global $koneksi;
$rumah_ibadah_id = (int) $rumah_ibadah_id;
$stmt = $koneksi->prepare("UPDATE penerima_bantuan SET rumah_ibadah_id=NULL, jarak_meter=NULL WHERE rumah_ibadah_id=? AND status_pengajuan IN ('menunggu','diterima')");
$stmt->bind_param('i', $rumah_ibadah_id);
$stmt->execute();
$affected = $stmt->affected_rows;
// Setelah dilepas, cari ulang rumah ibadah aktif terdekat lain jika ada.
sync_all_penerima_area();
return $affected;
}
?>