Files
2026-06-10 23:58:20 +07:00

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_KEMISKINAN') ?: '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';
}