initiall comit

This commit is contained in:
unknown
2026-06-12 22:44:23 +07:00
parent 4949c84feb
commit d7dd18ddba
9 changed files with 1069 additions and 15 deletions
+402
View File
@@ -0,0 +1,402 @@
<?php
// ========================================
// FILE: php/db_check.php
// Skrip Diagnostik dan Sinkronisasi Database
// ========================================
include 'koneksi.php';
// Timpa header dari koneksi.php agar browser merender HTML dengan benar
header('Content-Type: text/html; charset=utf-8');
// Matikan output JSON bawaan koneksi.php jika ada error, kita buat custom HTML UI
ob_start();
$errors = [];
$success_logs = [];
$table_status = [];
// Daftar tabel yang harus ada
$required_tables = [
'masyarakat_miskin',
'rumah_ibadah',
'bantuan_riwayat',
'bpjs_record',
'users',
'fasilitas_jalan',
'fasilitas_polygon',
'fasilitas_spbu'
];
// 1. Cek Koneksi Database
if ($conn->connect_error) {
$errors[] = "Koneksi database gagal: " . $conn->connect_error;
} else {
$success_logs[] = "Koneksi ke database `{$database}` berhasil!";
}
// 2. Baca file database.sql untuk mengambil DDL jika tabel perlu dibuat
$sql_content = '';
$sql_file = __DIR__ . '/../database.sql';
if (file_exists($sql_file)) {
$sql_content = file_get_contents($sql_file);
$success_logs[] = "File `database.sql` ditemukan dan berhasil dibaca.";
} else {
$errors[] = "File `database.sql` tidak ditemukan di {$sql_file}. Skrip akan menggunakan DDL cadangan.";
}
// Helper untuk eksekusi query pembuatan tabel cadangan jika database.sql tidak ada
$fallback_ddl = [
'masyarakat_miskin' => "CREATE TABLE IF NOT EXISTS `masyarakat_miskin` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(100) NOT NULL,
`tanggal_lahir` date NOT NULL,
`jenis_kelamin` enum('Laki-laki','Perempuan') NOT NULL,
`nik` varchar(20),
`alamat` text NOT NULL,
`latitude` decimal(10,8) NOT NULL,
`longitude` decimal(11,8) NOT NULL,
`status_bpjs` enum('Ya','Tidak','Proses') DEFAULT 'Tidak',
`no_hp` varchar(15),
`pekerjaan` varchar(100),
`pendapatan_bulanan` int(11),
`jumlah_tanggungan` int(11),
`status_rumah` enum('Milik Sendiri','Sewa','Menumpang') DEFAULT 'Menumpang',
`kelurahan` varchar(100),
`kecamatan` varchar(100),
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
'rumah_ibadah' => "CREATE TABLE IF NOT EXISTS `rumah_ibadah` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(100) NOT NULL,
`jenis` enum('Masjid','Gereja','Vihara','Kelenteng','Sinagog','Lain') NOT NULL,
`latitude` decimal(10,8) NOT NULL,
`longitude` decimal(11,8) NOT NULL,
`alamat` text NOT NULL,
`nama_pemimpin` varchar(100),
`no_hp_pemimpin` varchar(15),
`kapasitas` int(11),
`tahun_didirikan` year,
`keterangan` text,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
'bantuan_riwayat' => "CREATE TABLE IF NOT EXISTS `bantuan_riwayat` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`id_masyarakat` int(11) NOT NULL,
`jenis_bantuan` varchar(100) NOT NULL,
`tanggal_bantuan` date NOT NULL,
`nilai` int(11) NOT NULL,
`satuan` varchar(50),
`keterangan` text,
`nama_pemberi` varchar(100),
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`id_masyarakat`) REFERENCES `masyarakat_miskin`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
'bpjs_record' => "CREATE TABLE IF NOT EXISTS `bpjs_record` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`id_masyarakat` int(11) NOT NULL,
`no_peserta` varchar(20) NOT NULL UNIQUE,
`jenis_bpjs` enum('Kesehatan','Ketenagakerjaan','Jaminan Hari Tua') DEFAULT 'Kesehatan',
`tingkat` enum('Penerima Bantuan Iuran (PBI)','Pekerja Penerima Upah','Pekerja Bukan Penerima Upah','Peserta Mandiri') DEFAULT 'Penerima Bantuan Iuran (PBI)',
`tanggal_daftar` date NOT NULL,
`status_aktif` enum('Aktif','Nonaktif','Suspended') DEFAULT 'Aktif',
`faskes_tingkat_1` varchar(100),
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`id_masyarakat`) REFERENCES `masyarakat_miskin`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
'users' => "CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` varchar(50) NOT NULL UNIQUE,
`password` varchar(255) NOT NULL,
`nama_lengkap` varchar(100) NOT NULL,
`role` enum('Admin','Surveyor','Viewer') DEFAULT 'Surveyor',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
'fasilitas_jalan' => "CREATE TABLE IF NOT EXISTS `fasilitas_jalan` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(150) NOT NULL,
`geojson` text NOT NULL,
`tipe` varchar(50),
`keterangan` text,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
'fasilitas_polygon' => "CREATE TABLE IF NOT EXISTS `fasilitas_polygon` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(150) NOT NULL,
`geojson` text NOT NULL,
`tipe` varchar(50),
`keterangan` text,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
'fasilitas_spbu' => "CREATE TABLE IF NOT EXISTS `fasilitas_spbu` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(150) NOT NULL,
`latitude` decimal(10,8) NOT NULL,
`longitude` decimal(11,8) NOT NULL,
`alamat` text,
`is_24_jam` enum('Ya','Tidak') DEFAULT 'Tidak',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
];
// Cek dan buat tabel jika belum ada
foreach ($required_tables as $table) {
$check_table = $conn->query("SHOW TABLES LIKE '{$table}'");
if ($check_table && $check_table->num_rows > 0) {
// Tabel ada, cek jumlah baris
$count_res = $conn->query("SELECT COUNT(*) as total FROM `{$table}`");
$count = $count_res ? $count_res->fetch_assoc()['total'] : 0;
$table_status[$table] = [
'exists' => true,
'count' => $count,
'action' => 'No action needed'
];
} else {
// Tabel tidak ada, coba buat dari file SQL
$created = false;
if (!empty($sql_content)) {
// parsing DDL dari sql_content secara sederhana (karena multi_query bisa lambat / error untuk tabel spesifik)
// Sebagai alternatif, jalankan DDL cadangan agar 100% aman dan presisi
$ddl = $fallback_ddl[$table] ?? '';
if ($ddl && $conn->query($ddl)) {
$created = true;
}
} else {
$ddl = $fallback_ddl[$table] ?? '';
if ($ddl && $conn->query($ddl)) {
$created = true;
}
}
if ($created) {
$table_status[$table] = [
'exists' => true,
'count' => 0,
'action' => 'Table created successfully'
];
$success_logs[] = "Tabel `{$table}` berhasil dibuat karena tidak ditemukan.";
} else {
$table_status[$table] = [
'exists' => false,
'count' => 0,
'action' => 'Failed to create: ' . $conn->error
];
$errors[] = "Gagal membuat tabel `{$table}`: " . $conn->error;
}
}
}
// 3. Cek Default Admin User
if (isset($table_status['users']) && $table_status['users']['exists']) {
$check_admin = $conn->query("SELECT id FROM users WHERE username = 'admin'");
if ($check_admin && $check_admin->num_rows === 0) {
$insert_admin = $conn->query("INSERT INTO `users` (`username`, `password`, `nama_lengkap`, `role`) VALUES
('admin', '5f4dcc3b5aa765d61d8327deb882cf99', 'Administrator', 'Admin')");
if ($insert_admin) {
$success_logs[] = "Akun default admin berhasil ditambahkan.";
$table_status['users']['count'] += 1;
} else {
$errors[] = "Gagal menambahkan akun default admin: " . $conn->error;
}
}
}
// 4. Masukkan Data Sampel jika tabel kosong
if (isset($table_status['masyarakat_miskin']) && $table_status['masyarakat_miskin']['exists'] && $table_status['masyarakat_miskin']['count'] == 0) {
$insert_sampel = $conn->query("INSERT INTO `masyarakat_miskin`
(`id`, `nama`, `tanggal_lahir`, `jenis_kelamin`, `nik`, `alamat`, `latitude`, `longitude`, `status_bpjs`, `no_hp`, `pekerjaan`, `pendapatan_bulanan`, `jumlah_tanggungan`, `status_rumah`, `kelurahan`, `kecamatan`) VALUES
(1, 'Siti Nur Azizah', '1985-03-15', 'Perempuan', '6101234567890123', 'Jl. Mahakam No. 25', -0.06120000, 109.34450000, 'Ya', '081234567890', 'Buruh Harian', 1500000, 4, 'Menumpang', 'Sungai Jawi', 'Pontianak Kota')");
if ($insert_sampel) {
$success_logs[] = "Data sampel warga `Siti Nur Azizah` berhasil ditambahkan.";
$table_status['masyarakat_miskin']['count'] = 1;
// Tambahkan juga riwayat bantuan dan BPJS record yang cocok
$conn->query("INSERT INTO `bpjs_record` (`id_masyarakat`, `no_peserta`, `jenis_bpjs`, `tingkat`, `tanggal_daftar`, `status_aktif`, `faskes_tingkat_1`) VALUES
(1, '0001234567890', 'Kesehatan', 'Penerima Bantuan Iuran (PBI)', '2020-01-15', 'Aktif', 'Puskesmas Pontianak Kota')");
$conn->query("INSERT INTO `bantuan_riwayat` (`id_masyarakat`, `jenis_bantuan`, `tanggal_bantuan`, `nilai`, `satuan`, `keterangan`, `nama_pemberi`) VALUES
(1, 'Bantuan Pangan Non Tunai (BPNT)', '2026-05-10', 200000, 'Rupiah/Bulan', 'Bantuan Sembako tahap V', 'Dinas Sosial')");
$success_logs[] = "Data sampel BPJS & Riwayat Bantuan berhasil dihubungkan ke warga.";
}
}
if (isset($table_status['rumah_ibadah']) && $table_status['rumah_ibadah']['exists'] && $table_status['rumah_ibadah']['count'] == 0) {
$insert_sampel_ibadah = $conn->query("INSERT INTO `rumah_ibadah` (`nama`, `jenis`, `latitude`, `longitude`, `alamat`, `nama_pemimpin`, `no_hp_pemimpin`, `kapasitas`, `tahun_didirikan`) VALUES
('Masjid Al-Hidayah', 'Masjid', -0.06150000, 109.34500000, 'Jl. Diponegoro No. 45', 'Ustaz Ahmad', '081234567890', 500, 2005),
('Gereja Hati Kudus', 'Gereja', -0.06100000, 109.34400000, 'Jl. Jend Sudirman No. 12', 'Pdt. Bonaventura', '081234567891', 300, 1995)");
if ($insert_sampel_ibadah) {
$success_logs[] = "Data sampel rumah ibadah (Masjid & Gereja) berhasil ditambahkan.";
$table_status['rumah_ibadah']['count'] = 2;
}
}
// Bersihkan buffer output
ob_end_clean();
// Tampilkan halaman Laporan Diagnostik
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Database Deployment Diagnostic - WebGIS</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Outfit', sans-serif;
background-color: #0f172a;
color: #e2e8f0;
margin: 0;
padding: 40px 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: rgba(30, 41, 59, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
}
h1 {
color: #38bdf8;
font-weight: 800;
margin-top: 0;
border-bottom: 2px solid rgba(56, 189, 248, 0.3);
padding-bottom: 15px;
}
.status-badge {
display: inline-block;
padding: 5px 10px;
border-radius: 6px;
font-size: 12px;
font-weight: bold;
}
.status-ok {
background-color: #059669;
color: #ecfdf5;
}
.status-error {
background-color: #dc2626;
color: #fef2f2;
}
.status-info {
background-color: #2563eb;
color: #eff6ff;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
th, td {
text-align: left;
padding: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
th {
background-color: rgba(56, 189, 248, 0.1);
color: #38bdf8;
}
.log-section {
background-color: rgba(15, 23, 42, 0.6);
border-radius: 8px;
padding: 15px;
margin-top: 25px;
font-family: monospace;
font-size: 13px;
max-height: 250px;
overflow-y: auto;
border: 1px solid rgba(255, 255, 255, 0.05);
}
.log-success { color: #34d399; margin-bottom: 5px; }
.log-error { color: #f87171; margin-bottom: 5px; }
.btn {
display: inline-block;
background: linear-gradient(135deg, #38bdf8 0%, #0284c7 100%);
color: white;
text-decoration: none;
padding: 12px 24px;
border-radius: 8px;
font-weight: 600;
margin-top: 20px;
transition: all 0.3s ease;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(56, 189, 248, 0.4);
}
</style>
</head>
<body>
<div class="container">
<h1>📊 Status Diagnostik Database WebGIS</h1>
<h3>Hasil Pengecekan Tabel</h3>
<table>
<thead>
<tr>
<th>Nama Tabel</th>
<th>Status Keberadaan</th>
<th>Jumlah Baris Data</th>
<th>Tindakan Sistem</th>
</tr>
</thead>
<tbody>
<?php foreach ($required_tables as $table):
$status = $table_status[$table] ?? ['exists' => false, 'count' => 0, 'action' => 'Error'];
?>
<tr>
<td><strong><?php echo htmlspecialchars($table); ?></strong></td>
<td>
<?php if ($status['exists']): ?>
<span class="status-badge status-ok">Tersedia</span>
<?php else: ?>
<span class="status-badge status-error">Tidak Ditemukan</span>
<?php endif; ?>
</td>
<td><?php echo (int)$status['count']; ?> baris</td>
<td>
<span class="status-badge <?php echo $status['action'] === 'No action needed' ? 'status-ok' : 'status-info'; ?>">
<?php echo htmlspecialchars($status['action']); ?>
</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="log-section">
<h4 style="margin: 0 0 10px 0; color: #38bdf8; font-family: sans-serif;">Logs Sistem:</h4>
<?php foreach ($success_logs as $log): ?>
<div class="log-success">[SUCCESS] <?php echo htmlspecialchars($log); ?></div>
<?php endforeach; ?>
<?php foreach ($errors as $err): ?>
<div class="log-error">[ERROR] <?php echo htmlspecialchars($err); ?></div>
<?php endforeach; ?>
</div>
<div style="text-align: center;">
<a href="../index.html" class="btn">Kembali ke Portal WebGIS &rarr;</a>
</div>
</div>
</body>
</html>
+444
View File
@@ -0,0 +1,444 @@
<?php
// ========================================
// FILE: php/db_tool.php
// Alat Manajemen & Perbaikan Database (Production Ready)
// ========================================
include 'koneksi.php';
// Timpa header dari koneksi.php agar browser merender HTML dengan benar
header('Content-Type: text/html; charset=utf-8');
// Ambil PIN keamanan dari environment variable, default ke "123456" jika belum diatur
$secure_pin = getenv('DB_TOOL_PIN') ?: "123456";
// Mulai session untuk authentikasi pin
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$authenticated = isset($_SESSION['db_tool_auth']) && $_SESSION['db_tool_auth'] === true;
// Proses Login PIN
if (isset($_POST['pin'])) {
if ($_POST['pin'] === $secure_pin) {
$_SESSION['db_tool_auth'] = true;
$authenticated = true;
} else {
$login_error = "PIN yang Anda masukkan salah!";
}
}
// Proses Logout
if (isset($_GET['logout'])) {
$_SESSION['db_tool_auth'] = false;
session_destroy();
header("Location: db_tool.php");
exit();
}
// Daftar tabel wajib
$required_tables = [
'masyarakat_miskin',
'rumah_ibadah',
'bantuan_riwayat',
'bpjs_record',
'users',
'fasilitas_jalan',
'fasilitas_polygon',
'fasilitas_spbu'
];
$logs = [];
$error_logs = [];
// Fungsi bantu eksekusi perintah SQL secara massal
function executeSQLFile($conn, $filepath, &$logs, &$error_logs) {
if (!file_exists($filepath)) {
$error_logs[] = "File SQL tidak ditemukan di: " . basename($filepath);
return false;
}
$sql = file_get_contents($filepath);
// Nonaktifkan foreign key checks sementara untuk menghindari error dependensi saat drop/create
$conn->query("SET FOREIGN_KEY_CHECKS = 0;");
if ($conn->multi_query($sql)) {
do {
if ($res = $conn->store_result()) {
$res->free();
}
} while ($conn->more_results() && $conn->next_result());
$conn->query("SET FOREIGN_KEY_CHECKS = 1;");
$logs[] = "File SQL " . basename($filepath) . " berhasil diimpor sepenuhnya.";
return true;
} else {
$conn->query("SET FOREIGN_KEY_CHECKS = 1;");
$error_logs[] = "Gagal mengimpor SQL: " . $conn->error;
return false;
}
}
// Handler Aksi Perbaikan Database (Hanya berjalan jika terautentikasi)
$action_result = null;
if ($authenticated && isset($_POST['action'])) {
$action = $_POST['action'];
if ($action === 'check_connection') {
if ($conn->ping()) {
$logs[] = "Koneksi ke database server aktif! Server info: " . $conn->server_info;
} else {
$error_logs[] = "Koneksi database terputus: " . $conn->error;
}
}
elseif ($action === 'fix_missing') {
$logs[] = "Mulai memeriksa tabel yang hilang...";
// Dapatkan DDL Cadangan
$sql_file = __DIR__ . '/../database.sql';
foreach ($required_tables as $table) {
$check = $conn->query("SHOW TABLES LIKE '{$table}'");
if ($check && $check->num_rows > 0) {
$logs[] = "Tabel `{$table}` sudah ada (Lewati).";
} else {
$logs[] = "Tabel `{$table}` hilang! Mencoba membuat tabel...";
// Kita coba jalankan inisialisasi tabel dari db_check fallback atau database.sql
// Agar aman, buat manual tabel dasar
$created = false;
if (file_exists($sql_file)) {
// Jika file sql ada, kita impor untuk membuat yang kurang
// Supaya tidak meng-drop tabel lain, di database.sql harus menggunakan CREATE TABLE IF NOT EXISTS
executeSQLFile($conn, $sql_file, $logs, $error_logs);
$created = true;
break;
}
}
}
$logs[] = "Pemeriksaan tabel selesai.";
}
elseif ($action === 'optimize_repair') {
$logs[] = "Mulai proses perbaikan & optimasi tabel...";
foreach ($required_tables as $table) {
$check = $conn->query("SHOW TABLES LIKE '{$table}'");
if ($check && $check->num_rows > 0) {
$conn->query("REPAIR TABLE `{$table}`");
$conn->query("OPTIMIZE TABLE `{$table}`");
$logs[] = "Tabel `{$table}` berhasil diperbaiki dan dioptimalkan.";
} else {
$error_logs[] = "Tabel `{$table}` tidak ditemukan untuk dioptimalkan.";
}
}
$logs[] = "Proses optimasi selesai.";
}
elseif ($action === 'hard_reset') {
$logs[] = "⚠️ Melakukan HARD RESET Database...";
// Drop all known tables
$conn->query("SET FOREIGN_KEY_CHECKS = 0;");
foreach ($required_tables as $table) {
$conn->query("DROP TABLE IF EXISTS `{$table}`");
$logs[] = "Menghapus tabel `{$table}` jika ada.";
}
$conn->query("SET FOREIGN_KEY_CHECKS = 1;");
// Impor ulang database.sql
$sql_file = __DIR__ . '/../database.sql';
if (executeSQLFile($conn, $sql_file, $logs, $error_logs)) {
$logs[] = "✅ Database berhasil di-reset ke kondisi awal.";
} else {
$error_logs[] = "❌ Hard reset gagal saat mengimpor skema.";
}
}
}
// Kumpulkan status database saat ini
$status_tables = [];
if ($authenticated && !$conn->connect_error) {
foreach ($required_tables as $table) {
$check = $conn->query("SHOW TABLES LIKE '{$table}'");
if ($check && $check->num_rows > 0) {
$count_res = $conn->query("SELECT COUNT(*) as total FROM `{$table}`");
$count = $count_res ? $count_res->fetch_assoc()['total'] : 0;
$status_tables[$table] = [
'exists' => true,
'count' => $count
];
} else {
$status_tables[$table] = [
'exists' => false,
'count' => 0
];
}
}
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Database Master Tool - WebGIS</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700;800&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Outfit', sans-serif;
background-color: #0b0f19;
color: #f1f5f9;
margin: 0;
padding: 40px 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: rgba(17, 24, 39, 0.8);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6);
backdrop-filter: blur(10px);
}
h1 {
color: #38bdf8;
font-weight: 800;
margin-top: 0;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding-bottom: 15px;
}
.btn-logout {
font-size: 13px;
padding: 6px 12px;
background: #ef4444;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
text-decoration: none;
}
.card {
background: rgba(30, 41, 59, 0.5);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #94a3b8;
}
input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px;
background: #0f172a;
color: white;
box-sizing: border-box;
font-size: 16px;
}
.btn {
display: inline-block;
background: linear-gradient(135deg, #38bdf8 0%, #0284c7 100%);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(56, 189, 248, 0.3);
}
.btn-warning {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
}
.btn-danger {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.status-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 6px;
font-size: 11px;
font-weight: bold;
}
.status-ok { background: #059669; color: #ecfdf5; }
.status-err { background: #dc2626; color: #fef2f2; }
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
text-align: left;
padding: 10px;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
th { color: #38bdf8; font-weight: 600; }
.terminal {
background: #020617;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px;
padding: 15px;
font-family: monospace;
font-size: 13px;
max-height: 300px;
overflow-y: auto;
color: #34d399;
}
.terminal-err { color: #f87171; }
.action-card {
display: flex;
flex-direction: column;
gap: 10px;
border-left: 4px solid #38bdf8;
padding-left: 15px;
}
</style>
</head>
<body>
<div class="container">
<h1>
<span>🛠️ Database Master Debug Tool</span>
<?php if ($authenticated): ?>
<a href="?logout=1" class="btn-logout">Logout / Lock</a>
<?php endif; ?>
</h1>
<?php if (!$authenticated): ?>
<!-- Form Verifikasi PIN -->
<div class="card" style="max-width: 400px; margin: 40px auto;">
<h3 style="margin-top: 0; color: #38bdf8;">Keamanan Diperlukan</h3>
<p style="color: #94a3b8; font-size: 14px;">Masukkan PIN Keamanan untuk mengakses fungsi diagnostik database.</p>
<?php if (isset($login_error)): ?>
<div style="background:#fef2f2; color:#b91c1c; padding:10px; border-radius:6px; margin-bottom:15px; font-size:14px;">
<?php echo htmlspecialchars($login_error); ?>
</div>
<?php endif; ?>
<form method="POST" action="">
<div class="form-group">
<label for="pin">PIN Keamanan</label>
<input type="password" id="pin" name="pin" placeholder="Masukkan PIN..." required autofocus>
</div>
<button type="submit" class="btn" style="width: 100%;">Masuk & Periksa</button>
</form>
<p style="text-align:center; font-size:12px; color:#64748b; margin-top:15px;">
* PIN default lokal adalah <code>123456</code> (Atur via <code>DB_TOOL_PIN</code> di .env)
</p>
</div>
<?php else: ?>
<!-- Halaman Kontrol Database Utama -->
<div class="grid">
<!-- Sektor Status Koneksi & Tabel -->
<div class="card" style="grid-column: span 2;">
<h3 style="margin-top: 0; color: #38bdf8;">📊 Status Tabel Database</h3>
<table>
<thead>
<tr>
<th>Nama Tabel</th>
<th>Keberadaan</th>
<th>Jumlah Data</th>
</tr>
</thead>
<tbody>
<?php foreach ($required_tables as $table):
$status = $status_tables[$table] ?? ['exists' => false, 'count' => 0];
?>
<tr>
<td><code><?php echo htmlspecialchars($table); ?></code></td>
<td>
<?php if ($status['exists']): ?>
<span class="status-badge status-ok">OK (Tersedia)</span>
<?php else: ?>
<span class="status-badge status-err">Hilang / Tidak Ada</span>
<?php endif; ?>
</td>
<td><?php echo (int)$status['count']; ?> Baris</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Sektor Aksi Kontrol -->
<div class="card">
<h3 style="margin-top: 0; color: #38bdf8;">⚡ Aksi Perbaikan</h3>
<div style="display:flex; flex-direction:column; gap:15px;">
<form method="POST" action="" class="action-card" style="border-left-color: #3b82f6;">
<input type="hidden" name="action" value="check_connection">
<strong>Konektivitas Database</strong>
<small style="color:#94a3b8;">Uji ulang ping ke server MySQL dan baca info status server.</small>
<button type="submit" class="btn">Ping Test</button>
</form>
<form method="POST" action="" class="action-card" style="border-left-color: #10b981;">
<input type="hidden" name="action" value="fix_missing">
<strong>Buat Tabel yang Hilang</strong>
<small style="color:#94a3b8;">Membuat tabel yang hilang secara otomatis tanpa menyentuh tabel yang sudah ada.</small>
<button type="submit" class="btn btn-warning">Auto-Fix Tabel</button>
</form>
<form method="POST" action="" class="action-card" style="border-left-color: #f59e0b;">
<input type="hidden" name="action" value="optimize_repair">
<strong>Perbaiki & Optimasi</strong>
<small style="color:#94a3b8;">Menjalankan perintah SQL REPAIR dan OPTIMIZE untuk memperbaiki data corrupt.</small>
<button type="submit" class="btn btn-warning">Repair & Optimize</button>
</form>
<form method="POST" action="" class="action-card" style="border-left-color: #ef4444;" onsubmit="return confirm('⚠️ PERINGATAN KERAS! Tindakan ini akan menghapus semua data Anda secara permanen dan mereset database. Lanjutkan?');">
<input type="hidden" name="action" value="hard_reset">
<strong>Reset Ulang (Hard Reset)</strong>
<small style="color:#ef4444; font-weight:bold;">Menghapus semua tabel dan mengimpor ulang skema awal dari database.sql.</small>
<button type="submit" class="btn btn-danger">Hapus & Reset Database</button>
</form>
</div>
</div>
</div>
<!-- Terminal Output Log -->
<?php if (!empty($logs) || !empty($error_logs)): ?>
<div class="card">
<h3 style="margin-top: 0; color: #34d399;">💻 Output Log Eksekusi</h3>
<div class="terminal">
<?php foreach ($logs as $log): ?>
<div>[INFO] <?php echo htmlspecialchars($log); ?></div>
<?php endforeach; ?>
<?php foreach ($error_logs as $err): ?>
<div class="terminal-err">[ERROR] <?php echo htmlspecialchars($err); ?></div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div style="text-align: center; margin-top: 20px;">
<a href="../index.html" style="color: #38bdf8; text-decoration: none; font-weight: 600;">&larr; Kembali ke Portal WebGIS</a>
</div>
<?php endif; ?>
</div>
</body>
</html>
+27 -11
View File
@@ -18,19 +18,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
// Baca dari Environment Variable (untuk deployment Coolify/server)
// Jika tidak ada, fallback ke konfigurasi XAMPP lokal
$host = getenv('DB_HOST') ?: "localhost";
$username = getenv('DB_USER') ?: "root";
$password = getenv('DB_PASSWORD') ?: "";
$database = getenv('DB_DATABASE') ?: "pemetaan_kemiskinan";
$port = getenv('DB_PORT') ?: 3306;
$host = getenv('DB_HOST') ?: ($_SERVER['DB_HOST'] ?? ($_ENV['DB_HOST'] ?? "localhost"));
$username = getenv('DB_USER') ?: ($_SERVER['DB_USER'] ?? ($_ENV['DB_USER'] ?? "root"));
$password = getenv('DB_PASSWORD') ?: ($_SERVER['DB_PASSWORD'] ?? ($_ENV['DB_PASSWORD'] ?? ""));
$database = getenv('DB_DATABASE') ?: ($_SERVER['DB_DATABASE'] ?? ($_ENV['DB_DATABASE'] ?? "pemetaan_kemiskinan"));
$port = getenv('DB_PORT') ?: ($_SERVER['DB_PORT'] ?? ($_ENV['DB_PORT'] ?? 3306));
// KONEKSI MYSQLI
$conn = new mysqli($host, $username, $password, $database, (int)$port);
// KONEKSI MYSQLI DENGAN LOGIKA FALLBACK
$conn = null;
// CEK KONEKSI
if ($conn->connect_error) {
sendJSON('error', 'Koneksi database gagal: ' . $conn->connect_error);
exit();
// Aktifkan throwing exception untuk mysqli agar try-catch berfungsi dengan baik
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
// 1. Coba koneksi utama sesuai konfigurasi Environment / Env File
$conn = new mysqli($host, $username, $password, $database, (int)$port);
} catch (Exception $e) {
// 2. Coba koneksi Fallback ke Docker Host (host.docker.internal)
try {
$conn = new mysqli("host.docker.internal", "root", "", "pemetaan_kemiskinan", 3306);
} catch (Exception $e_docker) {
// 3. Coba koneksi Fallback ke Localhost murni
try {
$conn = new mysqli("localhost", "root", "", "pemetaan_kemiskinan", 3306);
} catch (Exception $e_local) {
// Jika semua gagal, berikan respon error
sendJSON('error', 'Koneksi database utama & fallback gagal: ' . $e_local->getMessage());
exit();
}
}
}
// SET CHARSET UTF-8