initial commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Deny from all
|
||||
</IfModule>
|
||||
|
||||
# Disable directory listing
|
||||
Options -Indexes
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'host' => 'localhost',
|
||||
'user' => 'root',
|
||||
'pass' => 'password',
|
||||
'name' => 'webgis_poverty',
|
||||
'charset' => 'utf8mb4',
|
||||
];
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// api/config/database.php
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_OFF);
|
||||
|
||||
if (!function_exists('loadDatabaseConfig')) {
|
||||
function loadDatabaseConfig(): array {
|
||||
$config = [
|
||||
'host' => getenv('DB_HOST') ?: 'localhost',
|
||||
'user' => getenv('DB_USER') ?: 'root',
|
||||
'pass' => getenv('DB_PASS') ?: '',
|
||||
'name' => getenv('DB_NAME') ?: 'webgis_poverty',
|
||||
'charset' => getenv('DB_CHARSET') ?: 'utf8mb4',
|
||||
];
|
||||
|
||||
$localConfigFile = __DIR__ . '/database.local.php';
|
||||
if (is_file($localConfigFile)) {
|
||||
$localConfig = require $localConfigFile;
|
||||
if (is_array($localConfig)) {
|
||||
$config = array_merge($config, array_intersect_key($localConfig, $config));
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
$dbConfig = loadDatabaseConfig();
|
||||
|
||||
define('DB_HOST', $dbConfig['host']);
|
||||
define('DB_USER', $dbConfig['user']);
|
||||
define('DB_PASS', $dbConfig['pass']);
|
||||
define('DB_NAME', $dbConfig['name']);
|
||||
define('DB_CHARSET', $dbConfig['charset']);
|
||||
|
||||
function getDB(): mysqli {
|
||||
static $conn = null;
|
||||
if ($conn === null) {
|
||||
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
|
||||
if ($conn->connect_error) {
|
||||
error_log('DB connection failed: ' . $conn->connect_error);
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Koneksi database gagal'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
$conn->set_charset(DB_CHARSET);
|
||||
}
|
||||
return $conn;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
// api/data_jalan.php
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
require_once __DIR__ . '/helpers/response.php';
|
||||
setCORSHeaders();
|
||||
|
||||
$db = getDB();
|
||||
$method = getMethod();
|
||||
requireWriteAuth($method);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
|
||||
switch ($method) {
|
||||
|
||||
case 'GET':
|
||||
if ($id > 0) {
|
||||
$stmt = $db->prepare(
|
||||
"SELECT id, nama_jalan, status_jalan,
|
||||
panjang_meter, keterangan,
|
||||
CAST(geojson AS CHAR) AS geojson,
|
||||
created_at
|
||||
FROM data_jalan WHERE id = ?"
|
||||
);
|
||||
$stmt->bind_param('i', $id);
|
||||
$stmt->execute();
|
||||
$row = $stmt->get_result()->fetch_assoc();
|
||||
if (!$row) sendError('Data tidak ditemukan', 404);
|
||||
// Parse geojson string menjadi object
|
||||
$row['geojson'] = json_decode($row['geojson']);
|
||||
$row['panjang_meter'] = (float)$row['panjang_meter'];
|
||||
sendSuccess($row);
|
||||
} else {
|
||||
$rows = [];
|
||||
$res = $db->query(
|
||||
"SELECT id, nama_jalan, status_jalan,
|
||||
panjang_meter, keterangan,
|
||||
CAST(geojson AS CHAR) AS geojson,
|
||||
created_at
|
||||
FROM data_jalan ORDER BY created_at DESC"
|
||||
);
|
||||
while ($r = $res->fetch_assoc()) {
|
||||
$r['geojson'] = json_decode($r['geojson']);
|
||||
$r['panjang_meter'] = (float)$r['panjang_meter'];
|
||||
$rows[] = $r;
|
||||
}
|
||||
sendSuccess($rows);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$body = getBody();
|
||||
foreach (['nama_jalan','status_jalan','geojson','panjang_meter'] as $f) {
|
||||
if (!isset($body[$f])) sendError("Field '$f' wajib diisi");
|
||||
}
|
||||
// Validasi GeoJSON LineString
|
||||
$geo = is_array($body['geojson'])
|
||||
? $body['geojson']
|
||||
: json_decode($body['geojson'], true);
|
||||
if (!$geo || $geo['type'] !== 'LineString') {
|
||||
sendError('GeoJSON harus bertipe LineString');
|
||||
}
|
||||
$geoStr = json_encode($geo);
|
||||
$panjang = (float)$body['panjang_meter'];
|
||||
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO data_jalan
|
||||
(nama_jalan, status_jalan, panjang_meter, keterangan, geojson)
|
||||
VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->bind_param('ssdss',
|
||||
$body['nama_jalan'], $body['status_jalan'],
|
||||
$panjang, $body['keterangan'], $geoStr
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal menyimpan data jalan');
|
||||
sendSuccess(['id' => $db->insert_id], 'Data jalan berhasil disimpan', 201);
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$body = getBody();
|
||||
$geo = is_array($body['geojson'])
|
||||
? $body['geojson']
|
||||
: json_decode($body['geojson'], true);
|
||||
$geoStr = json_encode($geo);
|
||||
$panjang = (float)$body['panjang_meter'];
|
||||
|
||||
$stmt = $db->prepare(
|
||||
"UPDATE data_jalan
|
||||
SET nama_jalan=?, status_jalan=?,
|
||||
panjang_meter=?, keterangan=?, geojson=?
|
||||
WHERE id=?"
|
||||
);
|
||||
$stmt->bind_param('ssdssi',
|
||||
$body['nama_jalan'], $body['status_jalan'],
|
||||
$panjang, $body['keterangan'], $geoStr, $id
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal memperbarui data jalan');
|
||||
sendSuccess(null, 'Data jalan berhasil diperbarui');
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$stmt = $db->prepare("DELETE FROM data_jalan WHERE id=?");
|
||||
$stmt->bind_param('i', $id);
|
||||
executeOrFail($stmt, 'Gagal menghapus data jalan');
|
||||
$stmt->affected_rows > 0
|
||||
? sendSuccess(null, 'Data jalan berhasil dihapus')
|
||||
: sendError('Data tidak ditemukan', 404);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError('Method tidak diizinkan', 405);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
// api/data_persil.php
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
require_once __DIR__ . '/helpers/response.php';
|
||||
setCORSHeaders();
|
||||
|
||||
$db = getDB();
|
||||
$method = getMethod();
|
||||
requireWriteAuth($method);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
|
||||
switch ($method) {
|
||||
|
||||
case 'GET':
|
||||
if ($id > 0) {
|
||||
$stmt = $db->prepare(
|
||||
"SELECT id, nama_pemilik, no_sertifikat, jenis_hak,
|
||||
luas_m2, keterangan,
|
||||
CAST(geojson AS CHAR) AS geojson,
|
||||
created_at
|
||||
FROM data_parsil WHERE id = ?"
|
||||
);
|
||||
$stmt->bind_param('i', $id);
|
||||
$stmt->execute();
|
||||
$row = $stmt->get_result()->fetch_assoc();
|
||||
if (!$row) sendError('Data tidak ditemukan', 404);
|
||||
$row['geojson'] = json_decode($row['geojson']);
|
||||
$row['luas_m2'] = (float)$row['luas_m2'];
|
||||
sendSuccess($row);
|
||||
} else {
|
||||
$rows = [];
|
||||
$res = $db->query(
|
||||
"SELECT id, nama_pemilik, no_sertifikat, jenis_hak,
|
||||
luas_m2, keterangan,
|
||||
CAST(geojson AS CHAR) AS geojson,
|
||||
created_at
|
||||
FROM data_parsil ORDER BY created_at DESC"
|
||||
);
|
||||
while ($r = $res->fetch_assoc()) {
|
||||
$r['geojson'] = json_decode($r['geojson']);
|
||||
$r['luas_m2'] = (float)$r['luas_m2'];
|
||||
$rows[] = $r;
|
||||
}
|
||||
sendSuccess($rows);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$body = getBody();
|
||||
foreach (['nama_pemilik','jenis_hak','geojson','luas_m2'] as $f) {
|
||||
if (!isset($body[$f])) sendError("Field '$f' wajib diisi");
|
||||
}
|
||||
$geo = is_array($body['geojson'])
|
||||
? $body['geojson']
|
||||
: json_decode($body['geojson'], true);
|
||||
if (!$geo || $geo['type'] !== 'Polygon') {
|
||||
sendError('GeoJSON harus bertipe Polygon');
|
||||
}
|
||||
$geoStr = json_encode($geo);
|
||||
$luas = (float)$body['luas_m2'];
|
||||
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO data_parsil
|
||||
(nama_pemilik, no_sertifikat, jenis_hak, luas_m2, keterangan, geojson)
|
||||
VALUES (?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->bind_param('sssdss',
|
||||
$body['nama_pemilik'], $body['no_sertifikat'],
|
||||
$body['jenis_hak'], $luas,
|
||||
$body['keterangan'], $geoStr
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal menyimpan data persil');
|
||||
sendSuccess(['id' => $db->insert_id], 'Data persil berhasil disimpan', 201);
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$body = getBody();
|
||||
$geo = is_array($body['geojson'])
|
||||
? $body['geojson']
|
||||
: json_decode($body['geojson'], true);
|
||||
$geoStr = json_encode($geo);
|
||||
$luas = (float)$body['luas_m2'];
|
||||
|
||||
$stmt = $db->prepare(
|
||||
"UPDATE data_parsil
|
||||
SET nama_pemilik=?, no_sertifikat=?, jenis_hak=?,
|
||||
luas_m2=?, keterangan=?, geojson=?
|
||||
WHERE id=?"
|
||||
);
|
||||
$stmt->bind_param('sssdssi',
|
||||
$body['nama_pemilik'], $body['no_sertifikat'],
|
||||
$body['jenis_hak'], $luas,
|
||||
$body['keterangan'], $geoStr, $id
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal memperbarui data persil');
|
||||
sendSuccess(null, 'Data persil berhasil diperbarui');
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$stmt = $db->prepare("DELETE FROM data_parsil WHERE id=?");
|
||||
$stmt->bind_param('i', $id);
|
||||
executeOrFail($stmt, 'Gagal menghapus data persil');
|
||||
$stmt->affected_rows > 0
|
||||
? sendSuccess(null, 'Data persil berhasil dihapus')
|
||||
: sendError('Data tidak ditemukan', 404);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError('Method tidak diizinkan', 405);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
// api/helpers/response.php
|
||||
|
||||
function setCORSHeaders(): void {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Vary: Origin');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Token');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
|
||||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||
$allowed = false;
|
||||
if ($origin !== '') {
|
||||
$origin = trim($origin);
|
||||
$allowedOrigins = parseAllowedOrigins(getenv('CORS_ALLOWED_ORIGINS') ?: '');
|
||||
if (!empty($allowedOrigins)) {
|
||||
$allowed = in_array($origin, $allowedOrigins, true);
|
||||
} else {
|
||||
$allowed = (bool)preg_match('/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/', $origin);
|
||||
}
|
||||
if ($allowed) {
|
||||
header('Access-Control-Allow-Origin: ' . $origin);
|
||||
}
|
||||
}
|
||||
|
||||
if (getMethod() === 'OPTIONS') {
|
||||
http_response_code(($origin !== '' && !$allowed) ? 403 : 204);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function parseAllowedOrigins(string $raw): array {
|
||||
$parts = array_map('trim', explode(',', $raw));
|
||||
return array_values(array_filter($parts, static fn ($v) => $v !== ''));
|
||||
}
|
||||
|
||||
function sendJSON(mixed $data, int $code = 200): void {
|
||||
http_response_code($code);
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
function sendSuccess(mixed $data = null, string $message = 'OK', int $code = 200): void {
|
||||
sendJSON(['status' => 'success', 'message' => $message, 'data' => $data], $code);
|
||||
}
|
||||
|
||||
function sendError(string $message, int $code = 400): void {
|
||||
sendJSON(['status' => 'error', 'message' => $message], $code);
|
||||
}
|
||||
|
||||
function requireWriteAuth(string $method): void {
|
||||
if (!in_array($method, ['POST', 'PUT', 'DELETE'], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$expected = (string)(getenv('API_WRITE_TOKEN') ?: '');
|
||||
if ($expected === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$provided = (string)($_SERVER['HTTP_X_API_TOKEN'] ?? '');
|
||||
if ($provided === '') {
|
||||
$auth = (string)($_SERVER['HTTP_AUTHORIZATION'] ?? '');
|
||||
if (preg_match('/Bearer\s+(.+)/i', $auth, $m)) {
|
||||
$provided = trim($m[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($provided === '' || !hash_equals($expected, $provided)) {
|
||||
sendError('Unauthorized', 401);
|
||||
}
|
||||
}
|
||||
|
||||
function executeOrFail(mysqli_stmt $stmt, string $defaultMessage = 'Operasi database gagal'): void {
|
||||
if ($stmt->execute()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$errno = $stmt->errno;
|
||||
$error = $stmt->error;
|
||||
error_log('DB stmt error [' . $errno . ']: ' . $error);
|
||||
|
||||
if ($errno === 1062) {
|
||||
sendError('Data duplikat terdeteksi', 409);
|
||||
}
|
||||
if ($errno === 1451 || $errno === 1452) {
|
||||
sendError('Relasi data tidak valid', 422);
|
||||
}
|
||||
sendError($defaultMessage, 500);
|
||||
}
|
||||
|
||||
function getBody(): array {
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = json_decode($raw, true);
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
|
||||
function getMethod(): string {
|
||||
return strtoupper($_SERVER['REQUEST_METHOD']);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
// api/log_bantuan.php
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
require_once __DIR__ . '/helpers/response.php';
|
||||
setCORSHeaders();
|
||||
|
||||
$db = getDB();
|
||||
$method = getMethod();
|
||||
requireWriteAuth($method);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
$pmId = isset($_GET['penduduk_miskin_id']) ? (int)$_GET['penduduk_miskin_id'] : 0;
|
||||
|
||||
switch ($method) {
|
||||
|
||||
case 'GET':
|
||||
if ($pmId > 0) {
|
||||
// Semua log untuk satu penduduk miskin
|
||||
$stmt = $db->prepare(
|
||||
"SELECT lb.*,
|
||||
ri.nama AS nama_rumah_ibadah,
|
||||
ri.jenis AS jenis_rumah_ibadah
|
||||
FROM log_bantuan lb
|
||||
JOIN rumah_ibadah ri ON ri.id = lb.rumah_ibadah_id
|
||||
WHERE lb.penduduk_miskin_id = ?
|
||||
ORDER BY lb.tanggal_bantuan DESC"
|
||||
);
|
||||
$stmt->bind_param('i', $pmId);
|
||||
$stmt->execute();
|
||||
$rows = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
|
||||
sendSuccess($rows);
|
||||
} elseif ($id > 0) {
|
||||
$stmt = $db->prepare(
|
||||
"SELECT lb.*,
|
||||
ri.nama AS nama_rumah_ibadah,
|
||||
pm.nama AS nama_penduduk
|
||||
FROM log_bantuan lb
|
||||
JOIN rumah_ibadah ri ON ri.id = lb.rumah_ibadah_id
|
||||
JOIN penduduk_miskin pm ON pm.id = lb.penduduk_miskin_id
|
||||
WHERE lb.id = ?"
|
||||
);
|
||||
$stmt->bind_param('i', $id);
|
||||
$stmt->execute();
|
||||
$row = $stmt->get_result()->fetch_assoc();
|
||||
$row ? sendSuccess($row) : sendError('Data tidak ditemukan', 404);
|
||||
} else {
|
||||
// Semua log bantuan dengan statistik
|
||||
$sql = "SELECT lb.*,
|
||||
ri.nama AS nama_rumah_ibadah,
|
||||
pm.nama AS nama_penduduk
|
||||
FROM log_bantuan lb
|
||||
JOIN rumah_ibadah ri ON ri.id = lb.rumah_ibadah_id
|
||||
JOIN penduduk_miskin pm ON pm.id = lb.penduduk_miskin_id
|
||||
ORDER BY lb.tanggal_bantuan DESC
|
||||
LIMIT 200";
|
||||
$rows = $db->query($sql)->fetch_all(MYSQLI_ASSOC);
|
||||
sendSuccess($rows);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$body = getBody();
|
||||
foreach (['penduduk_miskin_id','rumah_ibadah_id','tipe_bantuan','tanggal_bantuan'] as $f) {
|
||||
if (empty($body[$f])) sendError("Field '$f' wajib diisi");
|
||||
}
|
||||
$nilai = isset($body['nilai_bantuan']) && $body['nilai_bantuan'] !== ''
|
||||
? (float)$body['nilai_bantuan'] : null;
|
||||
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO log_bantuan
|
||||
(penduduk_miskin_id, rumah_ibadah_id, tipe_bantuan,
|
||||
sub_kategori, keterangan, tanggal_bantuan, nilai_bantuan)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->bind_param(
|
||||
'iissssd',
|
||||
$body['penduduk_miskin_id'],
|
||||
$body['rumah_ibadah_id'],
|
||||
$body['tipe_bantuan'],
|
||||
$body['sub_kategori'],
|
||||
$body['keterangan'],
|
||||
$body['tanggal_bantuan'],
|
||||
$nilai
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal mencatat log bantuan');
|
||||
sendSuccess(['id' => $db->insert_id], 'Log bantuan berhasil dicatat', 201);
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$stmt = $db->prepare("DELETE FROM log_bantuan WHERE id=?");
|
||||
$stmt->bind_param('i', $id);
|
||||
executeOrFail($stmt, 'Gagal menghapus log bantuan');
|
||||
$stmt->affected_rows > 0
|
||||
? sendSuccess(null, 'Log bantuan berhasil dihapus')
|
||||
: sendError('Data tidak ditemukan', 404);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError('Method tidak diizinkan', 405);
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
// api/penduduk_miskin.php
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
require_once __DIR__ . '/helpers/response.php';
|
||||
setCORSHeaders();
|
||||
|
||||
$db = getDB();
|
||||
$method = getMethod();
|
||||
requireWriteAuth($method);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
|
||||
// Endpoint khusus: recalc proximity semua penduduk miskin sekaligus
|
||||
if ($method === 'POST' && isset($_GET['action']) && $_GET['action'] === 'recalc_all') {
|
||||
requireWriteAuth('POST');
|
||||
$allPm = $db->query("SELECT id, latitude, longitude FROM penduduk_miskin");
|
||||
$updated = 0;
|
||||
while ($pm = $allPm->fetch_assoc()) {
|
||||
$prox = hitungProximity($db, (float)$pm['latitude'], (float)$pm['longitude']);
|
||||
$statusP = $prox['status'];
|
||||
$riId = $prox['rumah_ibadah_id'];
|
||||
$stmt = $db->prepare(
|
||||
"UPDATE penduduk_miskin SET status_proximity=?, rumah_ibadah_id=? WHERE id=?"
|
||||
);
|
||||
$stmt->bind_param('sii', $statusP, $riId, $pm['id']);
|
||||
$stmt->execute();
|
||||
$updated++;
|
||||
}
|
||||
sendSuccess(['updated' => $updated], "Proximity $updated data diperbarui");
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
|
||||
// ── GET ───────────────────────────────────────────────────────────
|
||||
case 'GET':
|
||||
if ($id > 0) {
|
||||
$stmt = $db->prepare(
|
||||
"SELECT pm.*,
|
||||
ri.nama AS nama_rumah_ibadah,
|
||||
ri.jenis AS jenis_rumah_ibadah
|
||||
FROM penduduk_miskin pm
|
||||
LEFT JOIN rumah_ibadah ri ON ri.id = pm.rumah_ibadah_id
|
||||
WHERE pm.id = ?"
|
||||
);
|
||||
$stmt->bind_param('i', $id);
|
||||
$stmt->execute();
|
||||
$row = $stmt->get_result()->fetch_assoc();
|
||||
if (!$row) sendError('Data tidak ditemukan', 404);
|
||||
$row['latitude'] = (float)$row['latitude'];
|
||||
$row['longitude'] = (float)$row['longitude'];
|
||||
|
||||
// Ambil ringkasan log bantuan
|
||||
$stmtLog = $db->prepare(
|
||||
"SELECT tipe_bantuan, COUNT(*) AS jumlah
|
||||
FROM log_bantuan
|
||||
WHERE penduduk_miskin_id = ?
|
||||
GROUP BY tipe_bantuan"
|
||||
);
|
||||
$stmtLog->bind_param('i', $id);
|
||||
$stmtLog->execute();
|
||||
$logs = [];
|
||||
$resLog = $stmtLog->get_result();
|
||||
while ($l = $resLog->fetch_assoc()) $logs[] = $l;
|
||||
$row['ringkasan_bantuan'] = $logs;
|
||||
|
||||
sendSuccess($row);
|
||||
} else {
|
||||
// GET semua + proximity recalculation cache
|
||||
$sql = "SELECT pm.id, pm.nama, pm.no_wa, pm.nik, pm.alamat,
|
||||
pm.jumlah_anggota, pm.latitude, pm.longitude,
|
||||
pm.status_proximity, pm.rumah_ibadah_id,
|
||||
ri.nama AS nama_rumah_ibadah,
|
||||
-- hitung berapa kali dapat bantuan
|
||||
(SELECT COUNT(*) FROM log_bantuan lb
|
||||
WHERE lb.penduduk_miskin_id = pm.id) AS total_bantuan,
|
||||
(SELECT COUNT(*) FROM log_bantuan lb
|
||||
WHERE lb.penduduk_miskin_id = pm.id
|
||||
AND lb.tipe_bantuan = 'Pemberdayaan') AS bantuan_pemberdayaan,
|
||||
(SELECT COUNT(*) FROM log_bantuan lb
|
||||
WHERE lb.penduduk_miskin_id = pm.id
|
||||
AND lb.tipe_bantuan = 'Konsumtif') AS bantuan_konsumtif
|
||||
FROM penduduk_miskin pm
|
||||
LEFT JOIN rumah_ibadah ri ON ri.id = pm.rumah_ibadah_id
|
||||
ORDER BY pm.created_at DESC";
|
||||
$result = $db->query($sql);
|
||||
$rows = [];
|
||||
while ($r = $result->fetch_assoc()) {
|
||||
$r['latitude'] = (float)$r['latitude'];
|
||||
$r['longitude'] = (float)$r['longitude'];
|
||||
$r['total_bantuan'] = (int)$r['total_bantuan'];
|
||||
$r['bantuan_pemberdayaan'] = (int)$r['bantuan_pemberdayaan'];
|
||||
$r['bantuan_konsumtif'] = (int)$r['bantuan_konsumtif'];
|
||||
$rows[] = $r;
|
||||
}
|
||||
sendSuccess($rows);
|
||||
}
|
||||
break;
|
||||
|
||||
// ── POST ──────────────────────────────────────────────────────────
|
||||
case 'POST':
|
||||
$body = getBody();
|
||||
foreach (['nama', 'latitude', 'longitude'] as $f) {
|
||||
if (empty($body[$f]) && $body[$f] !== 0) sendError("Field '$f' wajib diisi");
|
||||
}
|
||||
|
||||
// Hitung proximity ke semua rumah ibadah
|
||||
$proximity = hitungProximity($db, $body['latitude'], $body['longitude']);
|
||||
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO penduduk_miskin
|
||||
(nama, no_wa, nik, alamat, jumlah_anggota,
|
||||
latitude, longitude, status_proximity, rumah_ibadah_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$jumlah = isset($body['jumlah_anggota']) ? (int)$body['jumlah_anggota'] : 1;
|
||||
$statusP = $proximity['status'];
|
||||
$riId = $proximity['rumah_ibadah_id'];
|
||||
|
||||
$stmt->bind_param(
|
||||
'ssssiddsi',
|
||||
$body['nama'], $body['no_wa'], $body['nik'],
|
||||
$body['alamat'], $jumlah,
|
||||
$body['latitude'], $body['longitude'],
|
||||
$statusP, $riId
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal menyimpan data penduduk miskin');
|
||||
$newId = $db->insert_id;
|
||||
sendSuccess(
|
||||
['id' => $newId, 'proximity' => $proximity],
|
||||
'Penduduk Miskin berhasil disimpan',
|
||||
201
|
||||
);
|
||||
break;
|
||||
|
||||
// ── PUT ───────────────────────────────────────────────────────────
|
||||
case 'PUT':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$body = getBody();
|
||||
$proximity = hitungProximity($db, $body['latitude'], $body['longitude']);
|
||||
$statusP = $proximity['status'];
|
||||
$riId = $proximity['rumah_ibadah_id'];
|
||||
$jumlah = isset($body['jumlah_anggota']) ? (int)$body['jumlah_anggota'] : 1;
|
||||
|
||||
$stmt = $db->prepare(
|
||||
"UPDATE penduduk_miskin
|
||||
SET nama=?, no_wa=?, nik=?, alamat=?, jumlah_anggota=?,
|
||||
latitude=?, longitude=?, status_proximity=?, rumah_ibadah_id=?
|
||||
WHERE id=?"
|
||||
);
|
||||
$stmt->bind_param(
|
||||
'ssssiddsii',
|
||||
$body['nama'], $body['no_wa'], $body['nik'],
|
||||
$body['alamat'], $jumlah,
|
||||
$body['latitude'], $body['longitude'],
|
||||
$statusP, $riId, $id
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal memperbarui data penduduk miskin');
|
||||
sendSuccess(['proximity' => $proximity], 'Data berhasil diperbarui');
|
||||
break;
|
||||
|
||||
// ── DELETE ────────────────────────────────────────────────────────
|
||||
case 'DELETE':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$stmt = $db->prepare("DELETE FROM penduduk_miskin WHERE id=?");
|
||||
$stmt->bind_param('i', $id);
|
||||
executeOrFail($stmt, 'Gagal menghapus data penduduk miskin');
|
||||
$stmt->affected_rows > 0
|
||||
? sendSuccess(null, 'Data berhasil dihapus')
|
||||
: sendError('Data tidak ditemukan', 404);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError('Method tidak diizinkan', 405);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// HELPER: Hitung proximity penduduk miskin terhadap semua rumah ibadah
|
||||
// Menggunakan Haversine formula di PHP untuk validasi server-side
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
function hitungProximity(mysqli $db, float $lat, float $lng): array {
|
||||
$result = $db->query(
|
||||
"SELECT id, nama, latitude, longitude, radius_meter FROM rumah_ibadah"
|
||||
);
|
||||
$status = 'luar_radius';
|
||||
$riId = null;
|
||||
$riNama = null;
|
||||
$minDistAll = PHP_INT_MAX; // jarak ke RI terdekat (apapun statusnya)
|
||||
$minDistIn = PHP_INT_MAX; // jarak ke RI terdekat yang dalam radius
|
||||
|
||||
while ($ri = $result->fetch_assoc()) {
|
||||
$dist = haversine($lat, $lng, (float)$ri['latitude'], (float)$ri['longitude']);
|
||||
|
||||
// Lacak jarak terdekat secara keseluruhan (untuk field jarak_meter)
|
||||
if ($dist < $minDistAll) {
|
||||
$minDistAll = $dist;
|
||||
}
|
||||
|
||||
// Cek apakah titik ini masuk dalam radius RI ini
|
||||
if ($dist <= (int)$ri['radius_meter'] && $dist < $minDistIn) {
|
||||
$minDistIn = $dist;
|
||||
$status = 'dalam_radius';
|
||||
$riId = (int)$ri['id'];
|
||||
$riNama = $ri['nama'];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => $status,
|
||||
'rumah_ibadah_id' => $riId,
|
||||
'nama_rumah_ibadah' => $riNama,
|
||||
'jarak_meter' => round($minDistAll, 2)
|
||||
];
|
||||
}
|
||||
|
||||
function haversine(float $lat1, float $lng1, float $lat2, float $lng2): float {
|
||||
$R = 6371000; // radius bumi dalam meter
|
||||
$dLat = deg2rad($lat2 - $lat1);
|
||||
$dLng = deg2rad($lng2 - $lng1);
|
||||
$a = sin($dLat/2)**2
|
||||
+ cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng/2)**2;
|
||||
return $R * 2 * atan2(sqrt($a), sqrt(1-$a));
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
// api/rumah_ibadah.php
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
require_once __DIR__ . '/helpers/response.php';
|
||||
setCORSHeaders();
|
||||
|
||||
$db = getDB();
|
||||
$method = getMethod();
|
||||
requireWriteAuth($method);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
|
||||
switch ($method) {
|
||||
|
||||
// ── GET: ambil semua atau satu data ──────────────────────────────
|
||||
case 'GET':
|
||||
if ($id > 0) {
|
||||
$stmt = $db->prepare(
|
||||
"SELECT id, nama, jenis, alamat, no_wa,
|
||||
latitude, longitude, radius_meter,
|
||||
created_at
|
||||
FROM rumah_ibadah WHERE id = ?"
|
||||
);
|
||||
$stmt->bind_param('i', $id);
|
||||
$stmt->execute();
|
||||
$row = $stmt->get_result()->fetch_assoc();
|
||||
if ($row) {
|
||||
$row['latitude'] = (float)$row['latitude'];
|
||||
$row['longitude'] = (float)$row['longitude'];
|
||||
$row['radius_meter'] = (int)$row['radius_meter'];
|
||||
}
|
||||
$row ? sendSuccess($row) : sendError('Data tidak ditemukan', 404);
|
||||
} else {
|
||||
$sql = "SELECT id, nama, jenis, alamat, no_wa,
|
||||
latitude, longitude, radius_meter,
|
||||
created_at
|
||||
FROM rumah_ibadah ORDER BY created_at DESC";
|
||||
$result = $db->query($sql);
|
||||
$rows = [];
|
||||
while ($r = $result->fetch_assoc()) {
|
||||
$r['latitude'] = (float)$r['latitude'];
|
||||
$r['longitude'] = (float)$r['longitude'];
|
||||
$r['radius_meter'] = (int)$r['radius_meter'];
|
||||
$rows[] = $r;
|
||||
}
|
||||
sendSuccess($rows);
|
||||
}
|
||||
break;
|
||||
|
||||
// ── POST: tambah data baru ────────────────────────────────────────
|
||||
case 'POST':
|
||||
$body = getBody();
|
||||
$required = ['nama', 'jenis', 'latitude', 'longitude', 'radius_meter'];
|
||||
foreach ($required as $field) {
|
||||
if (empty($body[$field]) && $body[$field] !== 0) {
|
||||
sendError("Field '$field' wajib diisi");
|
||||
}
|
||||
}
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO rumah_ibadah
|
||||
(nama, jenis, alamat, no_wa, latitude, longitude, radius_meter)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->bind_param(
|
||||
'ssssddi',
|
||||
$body['nama'],
|
||||
$body['jenis'],
|
||||
$body['alamat'],
|
||||
$body['no_wa'],
|
||||
$body['latitude'],
|
||||
$body['longitude'],
|
||||
$body['radius_meter']
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal menyimpan rumah ibadah');
|
||||
$newId = $db->insert_id;
|
||||
sendSuccess(['id' => $newId], 'Rumah Ibadah berhasil disimpan', 201);
|
||||
break;
|
||||
|
||||
// ── PUT: update data ──────────────────────────────────────────────
|
||||
case 'PUT':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$body = getBody();
|
||||
$stmt = $db->prepare(
|
||||
"UPDATE rumah_ibadah
|
||||
SET nama=?, jenis=?, alamat=?, no_wa=?,
|
||||
latitude=?, longitude=?, radius_meter=?
|
||||
WHERE id=?"
|
||||
);
|
||||
$stmt->bind_param(
|
||||
'ssssddii',
|
||||
$body['nama'], $body['jenis'],
|
||||
$body['alamat'], $body['no_wa'],
|
||||
$body['latitude'], $body['longitude'],
|
||||
$body['radius_meter'], $id
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal memperbarui rumah ibadah');
|
||||
sendSuccess(null, 'Rumah Ibadah berhasil diperbarui');
|
||||
break;
|
||||
|
||||
// ── DELETE: hapus data ────────────────────────────────────────────
|
||||
case 'DELETE':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$stmt = $db->prepare("DELETE FROM rumah_ibadah WHERE id=?");
|
||||
$stmt->bind_param('i', $id);
|
||||
executeOrFail($stmt, 'Gagal menghapus rumah ibadah');
|
||||
$stmt->affected_rows > 0
|
||||
? sendSuccess(null, 'Rumah Ibadah berhasil dihapus')
|
||||
: sendError('Data tidak ditemukan', 404);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError('Method tidak diizinkan', 405);
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
// api/spbu.php
|
||||
require_once __DIR__ . '/config/database.php';
|
||||
require_once __DIR__ . '/helpers/response.php';
|
||||
setCORSHeaders();
|
||||
|
||||
$db = getDB();
|
||||
$method = getMethod();
|
||||
requireWriteAuth($method);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
|
||||
switch ($method) {
|
||||
|
||||
case 'GET':
|
||||
if ($id > 0) {
|
||||
$stmt = $db->prepare(
|
||||
"SELECT id, nama, no_wa, alamat, buka_24jam,
|
||||
latitude, longitude, created_at
|
||||
FROM spbu WHERE id = ?"
|
||||
);
|
||||
$stmt->bind_param('i', $id);
|
||||
$stmt->execute();
|
||||
$row = $stmt->get_result()->fetch_assoc();
|
||||
if (!$row) sendError('Data tidak ditemukan', 404);
|
||||
$row['latitude'] = (float)$row['latitude'];
|
||||
$row['longitude'] = (float)$row['longitude'];
|
||||
$row['buka_24jam']= (bool)$row['buka_24jam'];
|
||||
sendSuccess($row);
|
||||
} else {
|
||||
$rows = [];
|
||||
if (isset($_GET['buka_24jam'])) {
|
||||
$val = (int)$_GET['buka_24jam'];
|
||||
$stmt = $db->prepare(
|
||||
"SELECT id, nama, no_wa, alamat, buka_24jam,
|
||||
latitude, longitude, created_at
|
||||
FROM spbu WHERE buka_24jam = ? ORDER BY nama"
|
||||
);
|
||||
$stmt->bind_param('i', $val);
|
||||
$stmt->execute();
|
||||
$res = $stmt->get_result();
|
||||
} else {
|
||||
$res = $db->query(
|
||||
"SELECT id, nama, no_wa, alamat, buka_24jam,
|
||||
latitude, longitude, created_at
|
||||
FROM spbu ORDER BY nama"
|
||||
);
|
||||
}
|
||||
while ($r = $res->fetch_assoc()) {
|
||||
$r['latitude'] = (float)$r['latitude'];
|
||||
$r['longitude'] = (float)$r['longitude'];
|
||||
$r['buka_24jam']= (bool)$r['buka_24jam'];
|
||||
$rows[] = $r;
|
||||
}
|
||||
sendSuccess($rows);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$body = getBody();
|
||||
foreach (['nama','latitude','longitude'] as $f) {
|
||||
if (empty($body[$f]) && $body[$f] !== 0) sendError("Field '$f' wajib diisi");
|
||||
}
|
||||
$buka = isset($body['buka_24jam']) ? (int)(bool)$body['buka_24jam'] : 0;
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO spbu (nama, no_wa, alamat, buka_24jam, latitude, longitude)
|
||||
VALUES (?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->bind_param('sssidd',
|
||||
$body['nama'], $body['no_wa'], $body['alamat'],
|
||||
$buka, $body['latitude'], $body['longitude']
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal menyimpan data SPBU');
|
||||
sendSuccess(['id' => $db->insert_id], 'SPBU berhasil disimpan', 201);
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$body = getBody();
|
||||
$buka = isset($body['buka_24jam']) ? (int)(bool)$body['buka_24jam'] : 0;
|
||||
$stmt = $db->prepare(
|
||||
"UPDATE spbu
|
||||
SET nama=?, no_wa=?, alamat=?, buka_24jam=?, latitude=?, longitude=?
|
||||
WHERE id=?"
|
||||
);
|
||||
$stmt->bind_param('sssiddi',
|
||||
$body['nama'], $body['no_wa'], $body['alamat'],
|
||||
$buka, $body['latitude'], $body['longitude'], $id
|
||||
);
|
||||
executeOrFail($stmt, 'Gagal memperbarui data SPBU');
|
||||
sendSuccess(null, 'SPBU berhasil diperbarui');
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if ($id <= 0) sendError('ID wajib disertakan');
|
||||
$stmt = $db->prepare("DELETE FROM spbu WHERE id=?");
|
||||
$stmt->bind_param('i', $id);
|
||||
executeOrFail($stmt, 'Gagal menghapus data SPBU');
|
||||
$stmt->affected_rows > 0
|
||||
? sendSuccess(null, 'SPBU berhasil dihapus')
|
||||
: sendError('Data tidak ditemukan', 404);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError('Method tidak diizinkan', 405);
|
||||
}
|
||||
Reference in New Issue
Block a user