Fix: Convert 03PovertyMapping from submodule to regular folder
This commit is contained in:
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
/**
|
||||
* api/public/report.php
|
||||
* Public poverty reports — create (lapor.html) + admin CRUD.
|
||||
*/
|
||||
|
||||
// Matikan semua error reporting ke output
|
||||
error_reporting(0);
|
||||
ini_set('display_errors', 0);
|
||||
|
||||
// Bersihkan buffer
|
||||
while (ob_get_level()) ob_end_clean();
|
||||
ob_start();
|
||||
|
||||
// Load bootstrap (sudah include requireAuth() dan requireAdmin())
|
||||
if (!file_exists(__DIR__ . '/../../config/bootstrap.php')) {
|
||||
http_response_code(500);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Konfigurasi server tidak lengkap.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../config/bootstrap.php';
|
||||
|
||||
// Pastikan tidak ada output sebelum JSON
|
||||
if (ob_get_length()) ob_clean();
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
$action = $_GET['action'] ?? '';
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : null;
|
||||
|
||||
try {
|
||||
// POST tanpa action = create (dari lapor.html)
|
||||
if ($method === 'POST' && ($action === '' || $action === 'create')) {
|
||||
handleCreate();
|
||||
}
|
||||
// GET list
|
||||
elseif ($method === 'GET' && $action === 'list') {
|
||||
handleList();
|
||||
}
|
||||
// GET show
|
||||
elseif ($method === 'GET' && $action === 'show' && $id) {
|
||||
handleShow($id);
|
||||
}
|
||||
// POST approve (admin) - requireAdmin() dari bootstrap.php
|
||||
elseif ($method === 'POST' && $action === 'approve' && $id) {
|
||||
requireAdmin();
|
||||
handleApprove($id);
|
||||
}
|
||||
// POST reject (admin)
|
||||
elseif ($method === 'POST' && $action === 'reject' && $id) {
|
||||
requireAdmin();
|
||||
handleReject($id);
|
||||
}
|
||||
// POST delete (admin)
|
||||
elseif ($method === 'POST' && $action === 'delete' && $id) {
|
||||
requireAdmin();
|
||||
handleDelete($id);
|
||||
}
|
||||
elseif ($method === 'POST' && $action === 'delete_photo' && $id) {
|
||||
requireAuth(); // at minimum logged in
|
||||
handleDeletePhoto($id, 'public_reports', 'proof_photos',
|
||||
__DIR__ . '/../../uploads/reports/');
|
||||
}
|
||||
else {
|
||||
jsonError('Method or action not allowed', 405);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log('[public/report.php] PDO: ' . $e->getMessage());
|
||||
jsonError('Terjadi kesalahan database. Silakan coba lagi.', 500);
|
||||
} catch (Exception $e) {
|
||||
error_log('[public/report.php] ' . $e->getMessage());
|
||||
jsonError('Terjadi kesalahan server.', 500);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CREATE — public submission, all fields required
|
||||
// ============================================================
|
||||
function handleCreate(): void
|
||||
{
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = json_decode($raw, true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
jsonError('Data tidak valid.', 400);
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
|
||||
// Required fields
|
||||
$required = [
|
||||
'reporter_name' => 'Nama pelapor',
|
||||
'reporter_phone' => 'Nomor telepon',
|
||||
'head_name' => 'Nama kepala keluarga',
|
||||
'address' => 'Alamat',
|
||||
'kelurahan' => 'Kelurahan',
|
||||
'kecamatan' => 'Kecamatan',
|
||||
'description' => 'Deskripsi',
|
||||
'latitude' => 'Latitude',
|
||||
'longitude' => 'Longitude',
|
||||
'severity' => 'Tingkat urgensi'
|
||||
];
|
||||
|
||||
foreach ($required as $field => $label) {
|
||||
$value = trim($data[$field] ?? '');
|
||||
if ($value === '') {
|
||||
$errors[$field] = "$label wajib diisi.";
|
||||
}
|
||||
}
|
||||
|
||||
// RT/RW juga required
|
||||
if (trim($data['rt'] ?? '') === '') {
|
||||
$errors['rt'] = 'RT wajib diisi.';
|
||||
}
|
||||
if (trim($data['rw'] ?? '') === '') {
|
||||
$errors['rw'] = 'RW wajib diisi.';
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$firstError = reset($errors);
|
||||
jsonError($firstError, 422, ['validation_errors' => $errors]);
|
||||
}
|
||||
|
||||
// Phone validation
|
||||
$phone = preg_replace('/[\s\-\(\)\+]/', '', $data['reporter_phone']);
|
||||
if (!preg_match('/^\d{8,15}$/', $phone)) {
|
||||
jsonError('Nomor telepon tidak valid (8-15 digit).', 422);
|
||||
}
|
||||
|
||||
// Description min 20 chars
|
||||
if (mb_strlen($data['description']) < 20) {
|
||||
jsonError('Deskripsi minimal 20 karakter.', 422);
|
||||
}
|
||||
|
||||
// Coordinates
|
||||
$lat = (float)$data['latitude'];
|
||||
$lng = (float)$data['longitude'];
|
||||
if ($lat < -90 || $lat > 90 || $lat == 0) {
|
||||
jsonError('Latitude tidak valid.', 422);
|
||||
}
|
||||
if ($lng < -180 || $lng > 180 || $lng == 0) {
|
||||
jsonError('Longitude tidak valid.', 422);
|
||||
}
|
||||
|
||||
// Severity validation
|
||||
$allowedSeverity = ['ringan', 'sedang', 'berat', 'kritis'];
|
||||
if (!in_array($data['severity'], $allowedSeverity)) {
|
||||
jsonError('Tingkat urgensi tidak valid.', 422);
|
||||
}
|
||||
// Photo requirement
|
||||
$photoCount = (int)($data['proof_photo_count'] ?? -1);
|
||||
if ($photoCount === 0) {
|
||||
jsonError('Foto bukti wajib diunggah minimal 1 foto.', 422);
|
||||
}
|
||||
|
||||
// Rate limit
|
||||
$pdo = Database::get();
|
||||
$ip = getClientIP();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM public_reports WHERE ip_address = ? AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)");
|
||||
$stmt->execute([$ip]);
|
||||
if ((int)$stmt->fetchColumn() >= 5) {
|
||||
jsonError('Terlalu banyak laporan. Coba lagi besok.', 429);
|
||||
}
|
||||
|
||||
// Sanitize
|
||||
$sanitize = fn($s) => htmlspecialchars(strip_tags(trim($s)), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// Insert
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO public_reports
|
||||
(reporter_name, reporter_phone, rt, rw, head_name, address, kelurahan, kecamatan,
|
||||
latitude, longitude, description, severity, urgent_need, status, ip_address, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?, NOW())
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$sanitize($data['reporter_name']),
|
||||
$sanitize($data['reporter_phone']),
|
||||
$sanitize($data['rt'] ?? ''),
|
||||
$sanitize($data['rw'] ?? ''),
|
||||
$sanitize($data['head_name']),
|
||||
$sanitize($data['address']),
|
||||
$sanitize($data['kelurahan']),
|
||||
$sanitize($data['kecamatan']),
|
||||
round($lat, 7),
|
||||
round($lng, 7),
|
||||
$sanitize($data['description']),
|
||||
$data['severity'],
|
||||
$sanitize($data['urgent_need'] ?? ''),
|
||||
$ip
|
||||
]);
|
||||
|
||||
$newId = (int)$pdo->lastInsertId();
|
||||
|
||||
jsonSuccess(['id' => $newId], 'Laporan berhasil dikirim. Petugas akan segera memverifikasi.', 201);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// LIST
|
||||
// ============================================================
|
||||
function handleList(): void
|
||||
{
|
||||
$pdo = Database::get();
|
||||
$where = ['1=1'];
|
||||
$params = [];
|
||||
|
||||
if (!empty($_GET['status'])) {
|
||||
$where[] = 'status = ?';
|
||||
$params[] = $_GET['status'];
|
||||
}
|
||||
|
||||
$whereSQL = implode(' AND ', $where);
|
||||
$limit = min((int)($_GET['limit'] ?? 100), 500);
|
||||
$offset = max(0, (int)($_GET['offset'] ?? 0));
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT * FROM public_reports
|
||||
WHERE $whereSQL
|
||||
ORDER BY FIELD(status, 'pending', 'approved', 'rejected'), created_at DESC
|
||||
LIMIT $limit OFFSET $offset
|
||||
");
|
||||
$stmt->execute($params);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$cnt = $pdo->prepare("SELECT COUNT(*) FROM public_reports WHERE $whereSQL");
|
||||
$cnt->execute($params);
|
||||
|
||||
jsonSuccess(['reports' => $rows, 'total' => (int)$cnt->fetchColumn()]);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// SHOW
|
||||
// ============================================================
|
||||
function handleShow(int $id): void
|
||||
{
|
||||
$pdo = Database::get();
|
||||
$stmt = $pdo->prepare("SELECT * FROM public_reports WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row) {
|
||||
jsonError('Laporan tidak ditemukan.', 404);
|
||||
}
|
||||
|
||||
jsonSuccess($row);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// APPROVE
|
||||
// ============================================================
|
||||
function handleApprove(int $id): void
|
||||
{
|
||||
$pdo = Database::get();
|
||||
|
||||
// Get report
|
||||
$stmt = $pdo->prepare("SELECT * FROM public_reports WHERE id = ? AND status = 'pending'");
|
||||
$stmt->execute([$id]);
|
||||
$report = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$report) {
|
||||
jsonError('Laporan tidak ditemukan atau sudah diproses.', 404);
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// Create household
|
||||
$income = (int)($data['income'] ?? 0);
|
||||
$condition = $data['house_condition'] ?? 'tidak_layak';
|
||||
$education = $data['education'] ?? 'sd';
|
||||
|
||||
// Simple poverty score
|
||||
$score = 0;
|
||||
if ($income < 500000) $score += 30;
|
||||
elseif ($income < 1500000) $score += 20;
|
||||
elseif ($income < 3000000) $score += 10;
|
||||
if ($condition === 'tidak_layak') $score += 20;
|
||||
|
||||
$povertyStatus = match(true) {
|
||||
$score >= 60 => 'sangat_miskin',
|
||||
$score >= 40 => 'miskin',
|
||||
$score >= 20 => 'rentan_miskin',
|
||||
default => 'terdata'
|
||||
};
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO households
|
||||
(full_address, kelurahan, kecamatan, latitude, longitude, head_name,
|
||||
head_education, head_monthly_income, house_condition, land_ownership,
|
||||
poverty_score, poverty_status, notes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'numpang', ?, ?, ?)
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$report['address'],
|
||||
$report['kelurahan'] ?? '',
|
||||
$report['kecamatan'] ?? '',
|
||||
$report['latitude'],
|
||||
$report['longitude'],
|
||||
$report['head_name'],
|
||||
$education,
|
||||
$income,
|
||||
$condition,
|
||||
$score,
|
||||
$povertyStatus,
|
||||
'Dari laporan publik #' . $id
|
||||
]);
|
||||
|
||||
$householdId = (int)$pdo->lastInsertId();
|
||||
|
||||
// Update report
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE public_reports
|
||||
SET status = 'approved', converted_household_id = ?, admin_notes = ?, reviewed_at = NOW()
|
||||
WHERE id = ?
|
||||
");
|
||||
$stmt->execute([$householdId, $data['admin_notes'] ?? null, $id]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
jsonSuccess(['household_id' => $householdId], 'Laporan disetujui.');
|
||||
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// REJECT
|
||||
// ============================================================
|
||||
function handleReject(int $id): void
|
||||
{
|
||||
$pdo = Database::get();
|
||||
$data = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE public_reports SET status = 'rejected', admin_notes = ?, reviewed_at = NOW() WHERE id = ? AND status = 'pending'");
|
||||
$stmt->execute([$data['admin_notes'] ?? null, $id]);
|
||||
|
||||
if ($stmt->rowCount() === 0) {
|
||||
jsonError('Laporan tidak ditemukan atau sudah diproses.', 404);
|
||||
}
|
||||
|
||||
jsonSuccess(null, 'Laporan ditolak.');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// DELETE SINGLE PHOTO (shared by report & house endpoints)
|
||||
// ============================================================
|
||||
function handleDeletePhoto(int $id, string $table, string $col, string $dir): void
|
||||
{
|
||||
// Role guard: admin or field_officer only
|
||||
$user = requireAuth();
|
||||
$role = $user['role'] ?? '';
|
||||
if (!in_array($role, ['admin', 'field_officer'], true)) {
|
||||
jsonError('Akses ditolak. Hanya admin atau petugas lapangan.', 403);
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$filename = trim($data['filename'] ?? '');
|
||||
|
||||
// Security: filename must be a plain filename with no path separators
|
||||
if ($filename === '' || preg_match('/[\/\\\\]/', $filename) ||
|
||||
!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $filename)) {
|
||||
jsonError('Nama file tidak valid.', 400);
|
||||
}
|
||||
|
||||
// Must end with allowed extension
|
||||
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
if (!in_array($ext, ['jpg', 'jpeg', 'png'], true)) {
|
||||
jsonError('Ekstensi file tidak diizinkan.', 400);
|
||||
}
|
||||
|
||||
$pdo = Database::get();
|
||||
$stmt = $pdo->prepare("SELECT $col FROM $table WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row) {
|
||||
jsonError('Data tidak ditemukan.', 404);
|
||||
}
|
||||
|
||||
$photos = json_decode($row[$col] ?? '[]', true) ?: [];
|
||||
|
||||
// Check file is actually in this record's photo list (prevents arbitrary deletion)
|
||||
if (!in_array($filename, $photos, true)) {
|
||||
jsonError('File tidak ditemukan dalam data ini.', 404);
|
||||
}
|
||||
|
||||
// Remove from array
|
||||
$photos = array_values(array_filter($photos, fn($p) => $p !== $filename));
|
||||
|
||||
// Update DB
|
||||
$upd = $pdo->prepare("UPDATE $table SET $col = ? WHERE id = ?");
|
||||
$upd->execute([json_encode($photos, JSON_UNESCAPED_UNICODE), $id]);
|
||||
|
||||
// Delete physical file — realpath check prevents traversal
|
||||
$fullPath = realpath($dir . $filename);
|
||||
$realDir = realpath($dir);
|
||||
if ($fullPath && $realDir && str_starts_with($fullPath, $realDir)) {
|
||||
@unlink($fullPath);
|
||||
}
|
||||
|
||||
jsonSuccess(['remaining' => $photos], 'Foto berhasil dihapus.');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// DELETE
|
||||
// ============================================================
|
||||
function handleDelete(int $id): void
|
||||
{
|
||||
$pdo = Database::get();
|
||||
$stmt = $pdo->prepare("DELETE FROM public_reports WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
|
||||
if ($stmt->rowCount() === 0) {
|
||||
jsonError('Laporan tidak ditemukan.', 404);
|
||||
}
|
||||
|
||||
jsonSuccess(null, 'Laporan dihapus.');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HELPERS (Hanya fungsi yang tidak ada di bootstrap.php)
|
||||
// ============================================================
|
||||
|
||||
// getClientIP - tidak ada di bootstrap, aman didefinisikan
|
||||
function getClientIP(): string
|
||||
{
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
return trim(explode(',', $ip)[0]);
|
||||
}
|
||||
|
||||
// jsonSuccess dan jsonError - fungsi custom untuk response JSON
|
||||
function jsonSuccess($data, string $message = 'OK', int $code = 200): void
|
||||
{
|
||||
http_response_code($code);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
'data' => $data
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
function jsonError(string $message, int $code = 400, array $extra = []): void
|
||||
{
|
||||
http_response_code($code);
|
||||
$response = ['success' => false, 'message' => $message];
|
||||
if (!empty($extra)) {
|
||||
$response = array_merge($response, $extra);
|
||||
}
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
// ===== DEBUG MODE - FULL ERROR REPORTING =====
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$debug = [
|
||||
'step' => 1,
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
try {
|
||||
// Step 2: Cek method
|
||||
$debug['step'] = 2;
|
||||
$debug['method'] = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
throw new Exception('Method not allowed. Use POST.');
|
||||
}
|
||||
|
||||
// Step 3: Cek parameters
|
||||
$debug['step'] = 3;
|
||||
$debug['get'] = $_GET;
|
||||
$target = $_GET['target'] ?? '';
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
$debug['target'] = $target;
|
||||
$debug['id'] = $id;
|
||||
|
||||
if (!in_array($target, ['report', 'house'], true) || $id <= 0) {
|
||||
throw new Exception('Invalid target or id. target=' . $target . ', id=' . $id);
|
||||
}
|
||||
|
||||
// Step 4: Load bootstrap
|
||||
$debug['step'] = 4;
|
||||
$bootstrap_path = __DIR__ . '/../../config/bootstrap.php';
|
||||
$debug['bootstrap_path'] = $bootstrap_path;
|
||||
$debug['bootstrap_exists'] = file_exists($bootstrap_path);
|
||||
|
||||
if (!file_exists($bootstrap_path)) {
|
||||
throw new Exception('Bootstrap not found at: ' . $bootstrap_path);
|
||||
}
|
||||
|
||||
require_once $bootstrap_path;
|
||||
$debug['step'] = 5;
|
||||
$debug['bootstrap_loaded'] = true;
|
||||
|
||||
// Step 5: Authentication for house target
|
||||
$debug['step'] = 6;
|
||||
if ($target === 'house') {
|
||||
$user = requireAuth();
|
||||
$debug['auth_user'] = $user['name'] ?? 'unknown';
|
||||
}
|
||||
|
||||
// Step 6: Setup directories
|
||||
$debug['step'] = 7;
|
||||
$dir = $target === 'report' ? 'reports' : 'houses';
|
||||
$table = $target === 'report' ? 'public_reports' : 'households';
|
||||
$dest = __DIR__ . '/../../uploads/' . $dir . '/';
|
||||
$debug['dest'] = $dest;
|
||||
$debug['dest_exists'] = is_dir($dest);
|
||||
$debug['dest_writable'] = is_writable($dest);
|
||||
|
||||
if (!is_dir($dest)) {
|
||||
mkdir($dest, 0777, true);
|
||||
$debug['dest_created'] = true;
|
||||
}
|
||||
|
||||
// Step 7: Check record exists
|
||||
$debug['step'] = 8;
|
||||
$pdo = Database::get();
|
||||
$colName = $target === 'report' ? 'proof_photos' : 'house_photos';
|
||||
|
||||
$stmt = $pdo->prepare("SELECT id, $colName FROM $table WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$record = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$debug['record_exists'] = !empty($record);
|
||||
|
||||
if (!$record) {
|
||||
throw new Exception("Record with id $id not found in $table");
|
||||
}
|
||||
|
||||
$existing = json_decode($record[$colName] ?? '[]', true) ?: [];
|
||||
$debug['existing_photos'] = count($existing);
|
||||
|
||||
// Step 8: Check uploaded files
|
||||
$debug['step'] = 9;
|
||||
$files = $_FILES['photos'] ?? null;
|
||||
$debug['has_files'] = !empty($files);
|
||||
$debug['files_count'] = $files ? count($files['name']) : 0;
|
||||
|
||||
if (!$files || empty($files['name'][0])) {
|
||||
throw new Exception('No files uploaded');
|
||||
}
|
||||
|
||||
// Step 9: Process files
|
||||
$debug['step'] = 10;
|
||||
$saved = [];
|
||||
|
||||
foreach ($files['name'] as $i => $originalName) {
|
||||
if ($files['error'][$i] !== UPLOAD_ERR_OK) {
|
||||
throw new Exception('Upload error for file: ' . $originalName);
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
|
||||
$unique = bin2hex(random_bytes(12));
|
||||
$filename = "{$dir}_{$id}_{$unique}.{$ext}";
|
||||
$fullPath = $dest . $filename;
|
||||
|
||||
if (move_uploaded_file($files['tmp_name'][$i], $fullPath)) {
|
||||
$saved[] = $filename;
|
||||
$debug['saved'][] = $filename;
|
||||
} else {
|
||||
throw new Exception('Failed to move uploaded file: ' . $originalName);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 10: Update database
|
||||
$debug['step'] = 11;
|
||||
$all = array_merge($existing, $saved);
|
||||
$upd = $pdo->prepare("UPDATE $table SET $colName = ? WHERE id = ?");
|
||||
$upd->execute([json_encode($all, JSON_UNESCAPED_UNICODE), $id]);
|
||||
$debug['db_updated'] = true;
|
||||
|
||||
// Success response
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Foto berhasil diunggah',
|
||||
'data' => [
|
||||
'filenames' => $saved,
|
||||
'all_photos' => $all
|
||||
],
|
||||
'debug' => $debug
|
||||
], JSON_PRETTY_PRINT);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$debug['error'] = $e->getMessage();
|
||||
$debug['trace'] = $e->getTraceAsString();
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'debug' => $debug
|
||||
], JSON_PRETTY_PRINT);
|
||||
}
|
||||
Reference in New Issue
Block a user