178 lines
7.1 KiB
PHP
178 lines
7.1 KiB
PHP
<?php
|
|
// ============================================================
|
|
// Konfigurasi & Koneksi Database
|
|
// WebGIS Pengentasan Kemiskinan v2.0
|
|
// ============================================================
|
|
|
|
define('DB_HOST', getenv('DB_HOST') ?: 'localhost');
|
|
define('DB_PORT', getenv('DB_PORT') ?: '3306');
|
|
define('DB_NAME', getenv('DB_NAME') ?: 'webgis_kemiskinan');
|
|
define('DB_USER', getenv('DB_USER') ?: 'root');
|
|
define('DB_PASS', getenv('DB_PASSWORD') ?: '');
|
|
define('DB_CHARSET', 'utf8mb4');
|
|
|
|
define('APP_NAME', 'WebGIS Pengentasan Kemiskinan');
|
|
define('APP_VERSION', '2.0.0');
|
|
define('APP_URL', 'http://localhost/webgis-kemiskinan');
|
|
define('APP_ENV', 'development'); // Ganti ke 'production' saat deploy
|
|
|
|
define('UPLOAD_PATH', __DIR__ . '/../uploads/');
|
|
define('UPLOAD_BUKTI', UPLOAD_PATH . 'bukti/');
|
|
define('UPLOAD_LAPORAN', UPLOAD_PATH . 'foto_laporan/');
|
|
define('UPLOAD_FOTO', UPLOAD_PATH . 'foto_profil/');
|
|
define('MAX_FILE_SIZE', 5 * 1024 * 1024); // 5 MB
|
|
define('ALLOWED_IMG_EXT', ['jpg','jpeg','png','webp']);
|
|
define('ALLOWED_FILE_EXT', ['jpg','jpeg','png','webp','pdf']);
|
|
|
|
// Buat folder upload jika belum ada
|
|
foreach ([UPLOAD_BUKTI, UPLOAD_LAPORAN, UPLOAD_FOTO] as $dir) {
|
|
if (!is_dir($dir)) @mkdir($dir, 0755, true);
|
|
}
|
|
|
|
// ============================================================
|
|
// Koneksi PDO
|
|
// ============================================================
|
|
|
|
$dsn = "mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
|
|
|
|
$pdoOptions = [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
];
|
|
|
|
try {
|
|
$pdo = new PDO($dsn, DB_USER, DB_PASS, $pdoOptions);
|
|
} catch (PDOException $e) {
|
|
http_response_code(500);
|
|
header('Content-Type: application/json');
|
|
// Jangan expose detail error di produksi
|
|
$errMsg = (defined('APP_ENV') && APP_ENV === 'development') ? $e->getMessage() : 'Hubungi administrator sistem.';
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => 'Koneksi database gagal: ' . DB_NAME . '@' . DB_HOST . ' tidak ditemukan. Pastikan database sudah dibuat dan nama database benar.',
|
|
'error' => $errMsg
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// ============================================================
|
|
// Helper: Response JSON
|
|
// ============================================================
|
|
|
|
function jsonResponse(bool $success, string $message, array $data = [], int $httpCode = 200): void {
|
|
http_response_code($httpCode);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type, Authorization');
|
|
echo json_encode(array_merge(['success' => $success, 'message' => $message], $data), JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
// ============================================================
|
|
// Helper: Sanitasi & Validasi
|
|
// ============================================================
|
|
|
|
function h(string $s): string {
|
|
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
function sanitizeStr(?string $v, int $max = 255): string {
|
|
return mb_substr(trim($v ?? ''), 0, $max);
|
|
}
|
|
|
|
function validateNIK(string $nik): bool {
|
|
return preg_match('/^\d{16}$/', $nik) === 1;
|
|
}
|
|
|
|
function validateCoord(string $val, string $type = 'lat'): bool {
|
|
$f = (float)$val;
|
|
return $type === 'lat' ? ($f >= -90 && $f <= 90) : ($f >= -180 && $f <= 180);
|
|
}
|
|
|
|
// ============================================================
|
|
// Helper: Upload File
|
|
// ============================================================
|
|
|
|
function uploadFile(array $file, string $destDir, array $allowedExt = null): ?string {
|
|
if ($allowedExt === null) $allowedExt = ALLOWED_FILE_EXT;
|
|
if ($file['error'] !== UPLOAD_ERR_OK) return null;
|
|
if ($file['size'] > MAX_FILE_SIZE) throw new RuntimeException('Ukuran file melebihi 5MB.');
|
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
if (!in_array($ext, $allowedExt, true)) throw new RuntimeException("Format file .$ext tidak diizinkan.");
|
|
$filename = uniqid('img_', true) . '.' . $ext;
|
|
$dest = rtrim($destDir, '/') . '/' . $filename;
|
|
if (!move_uploaded_file($file['tmp_name'], $dest)) throw new RuntimeException('Gagal menyimpan file.');
|
|
return $filename;
|
|
}
|
|
|
|
// ============================================================
|
|
// Helper: Log Aktivitas
|
|
// ============================================================
|
|
|
|
function logAktivitas(PDO $pdo, ?int $userId, string $aksi, string $tabel = '', string $recordId = '', $dataLama = null, $dataBaru = null): void {
|
|
try {
|
|
$stmt = $pdo->prepare("
|
|
INSERT INTO log_aktivitas (user_id, aksi, tabel, record_id, data_lama, data_baru, ip_address, user_agent)
|
|
VALUES (:u, :a, :t, :r, :dl, :db, :ip, :ua)
|
|
");
|
|
$stmt->execute([
|
|
':u' => $userId,
|
|
':a' => $aksi,
|
|
':t' => $tabel,
|
|
':r' => $recordId,
|
|
':dl' => $dataLama ? json_encode($dataLama, JSON_UNESCAPED_UNICODE) : null,
|
|
':db' => $dataBaru ? json_encode($dataBaru, JSON_UNESCAPED_UNICODE) : null,
|
|
':ip' => $_SERVER['REMOTE_ADDR'] ?? null,
|
|
':ua' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255),
|
|
]);
|
|
} catch (Exception) { /* log gagal tidak menghentikan proses */ }
|
|
}
|
|
|
|
// ============================================================
|
|
// Validasi Logika Pendidikan vs Umur
|
|
// ============================================================
|
|
|
|
function validasiPendidikanUmur(int $umur, string $pendidikan, string $statusPendidikan): array {
|
|
$warnings = [];
|
|
$errors = [];
|
|
|
|
if ($umur >= 7 && $umur <= 12) {
|
|
if ($statusPendidikan !== 'sekolah') {
|
|
$warnings[] = "Anak usia $umur tahun semestinya masih sekolah SD.";
|
|
}
|
|
} elseif ($umur >= 13 && $umur <= 15) {
|
|
if ($statusPendidikan !== 'sekolah') {
|
|
$warnings[] = "Anak usia $umur tahun semestinya masih sekolah SMP.";
|
|
}
|
|
} elseif ($umur >= 16 && $umur <= 18) {
|
|
if ($statusPendidikan !== 'sekolah') {
|
|
$warnings[] = "Anak usia $umur tahun semestinya masih sekolah SMA/SMK.";
|
|
}
|
|
}
|
|
// Umur > 18: bebas, boleh tidak sekolah, boleh pelatihan
|
|
|
|
return ['warnings' => $warnings, 'errors' => $errors];
|
|
}
|
|
|
|
// ============================================================
|
|
// Prioritas Bantuan
|
|
// ============================================================
|
|
|
|
function hitungPrioritasBantuan(string $statusEkonomi, string $kondisiKesehatan, bool $belumPernahDibantu): string {
|
|
if ($statusEkonomi === 'miskin' && in_array($kondisiKesehatan, ['sakit_parah', 'disabilitas'])) {
|
|
return 'KRITIS';
|
|
}
|
|
if ($statusEkonomi === 'miskin' && $belumPernahDibantu) {
|
|
return 'TINGGI';
|
|
}
|
|
if ($statusEkonomi === 'miskin') {
|
|
return 'SEDANG';
|
|
}
|
|
if ($statusEkonomi === 'rentan' && in_array($kondisiKesehatan, ['sakit_parah', 'disabilitas'])) {
|
|
return 'SEDANG';
|
|
}
|
|
return 'RENDAH';
|
|
}
|