false, 'error' => 'No file uploaded']); exit; } $file = $_FILES['file']; $ext = pathinfo($file['name'], PATHINFO_EXTENSION); $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp']; if (!in_array(strtolower($ext), $allowed)) { echo json_encode(['success' => false, 'error' => 'Invalid file type']); exit; } $filename = time() . '_' . uniqid() . '.' . $ext; $targetDir = __DIR__ . '/../assets/images/chat/'; if (!is_dir($targetDir)) { mkdir($targetDir, 0777, true); } $targetPath = $targetDir . $filename; if (move_uploaded_file($file['tmp_name'], $targetPath)) { $imageUrl = '/assets/images/chat/' . $filename; $message = ''; if (isset($_SESSION['admin_id'])) { $user_id = (int)($_POST['user_id'] ?? 0); $ip = $_POST['ip_address'] ?? ''; $sid = $_POST['session_id'] ?? ''; if ($ip === '---') $ip = ''; // If IP is missing, try to get it from messages or fallback to current IP if (empty($ip) && empty($sid)) { if ($user_id != 0) { $stmt = db()->prepare("SELECT ip_address, session_id FROM messages WHERE user_id = ? AND ip_address != '' AND ip_address != '---' ORDER BY id DESC LIMIT 1"); $stmt->execute([$user_id]); $row = $stmt->fetch(); $ip = $row['ip_address'] ?? ''; $sid = $row['session_id'] ?? ''; } if (empty($ip)) { $ip = getRealIP(); } } $sender = 'admin'; $admin_id = $_SESSION['admin_id']; $stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?, ?)"); $stmt->execute([$user_id, $admin_id, $sender, $message, $ip, $sid]); } else { $user_id = (int)($_SESSION['user_id'] ?? 0); $ip = getRealIP(); $sid = session_id(); // Fallback: If user_id is 0 but we find a user with this registration IP, associate it if ($user_id === 0) { $stmt = db()->prepare("SELECT id FROM users WHERE registration_ip = ? OR last_login_ip = ? ORDER BY id DESC LIMIT 1"); $stmt->execute([$ip, $ip]); $user_id = (int)($stmt->fetchColumn() ?: 0); } $sender = 'user'; $stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$user_id, $sender, $message, $ip, $sid]); } $newId = db()->lastInsertId(); $createdAt = date('Y-m-d H:i:s'); // Update visitors table to ensure immediate visibility for both users and admins if ($sender === 'user') { $user_time = date('H:i:s'); $stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, session_id = VALUES(session_id)"); $stmt->execute([$user_id, $ip, $sid, $user_time]); } echo json_encode([ 'success' => true, 'url' => $imageUrl, 'message' => [ 'id' => $newId, 'sender' => $sender, 'message' => $message, 'created_at' => $createdAt ] ]); } else { echo json_encode(['success' => false, 'error' => 'Failed to move uploaded file']); } exit; } if ($action === 'get_messages') { $user_id = $_SESSION['user_id'] ?? 0; $target_user_id = $_GET['user_id'] ?? $user_id; $target_ip = $_GET['ip'] ?? getRealIP(); $target_sid = $_GET['session_id'] ?? ''; // If admin is requesting, we use the provided user_id and ip/session if (isset($_SESSION['admin_id'])) { $stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (session_id = ? AND session_id != '') OR (ip_address = ? AND ip_address != '' AND session_id = '') ORDER BY created_at ASC"); $stmt->execute([$target_user_id, $target_sid, $target_ip]); // Mark as read $stmt_read = db()->prepare("UPDATE messages SET is_read = 1 WHERE sender = 'user' AND ((user_id = ? AND user_id != 0) OR (session_id = ? AND session_id != '') OR (ip_address = ? AND ip_address != '' AND session_id = ''))"); $stmt_read->execute([$target_user_id, $target_sid, $target_ip]); } else { // User requesting their own messages $user_id = (int)($_SESSION['user_id'] ?? 0); $ip = getRealIP(); $sid = session_id(); // Fallback: If user_id is 0 but we find a user with this IP, associate it if ($user_id === 0) { $stmt = db()->prepare("SELECT id FROM users WHERE registration_ip = ? OR last_login_ip = ? ORDER BY id DESC LIMIT 1"); $stmt->execute([$ip, $ip]); $user_id = (int)($stmt->fetchColumn() ?: 0); } $stmt = db()->prepare("SELECT * FROM messages WHERE (user_id = ? AND user_id != 0) OR (session_id = ?) OR (ip_address = ? AND ip_address != '' AND session_id = '') ORDER BY created_at ASC"); $stmt->execute([$user_id, $sid, $ip]); } $messages = $stmt->fetchAll(); echo json_encode($messages); exit; } if ($action === 'send_message') { $message = $_POST['message'] ?? ''; if (!$message) exit(json_encode(['success' => false, 'error' => 'Empty message'])); $user_id = (int)($_SESSION['user_id'] ?? 0); $ip = getRealIP(); $sid = session_id(); // Fallback: If user_id is 0 but we find a user with this registration IP, associate it if ($user_id === 0) { $stmt = db()->prepare("SELECT id FROM users WHERE registration_ip = ? OR last_login_ip = ? ORDER BY id DESC LIMIT 1"); $stmt->execute([$ip, $ip]); $user_id = (int)($stmt->fetchColumn() ?: 0); } $stmt = db()->prepare("INSERT INTO messages (user_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$user_id, 'user', $message, $ip, $sid]); $newId = db()->lastInsertId(); // Also update visitors table to ensure immediate visibility $user_time = date('H:i:s'); $stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, session_id = VALUES(session_id)"); $stmt->execute([$user_id, $ip, $sid, $user_time]); echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => 'user', 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]); exit; } if ($action === 'mark_read') { if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized'])); $user_id = (int)($_POST['user_id'] ?? 0); $ip = $_POST['ip_address'] ?? ''; $sid = $_POST['session_id'] ?? ''; if ($ip === '---') $ip = ''; $stmt = db()->prepare("UPDATE messages SET is_read = 1 WHERE sender = 'user' AND is_read = 0 AND ((user_id = ? AND user_id != 0) OR (session_id = ? AND session_id != '') OR (ip_address = ? AND ip_address != '' AND session_id = ''))"); $stmt->execute([$user_id, $sid, $ip]); echo json_encode(['success' => true]); exit; } if ($action === 'admin_send') { if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized'])); $message = $_POST['message'] ?? ''; $user_id = (int)($_POST['user_id'] ?? 0); $target_ip = $_POST['ip_address'] ?? ''; $target_sid = $_POST['session_id'] ?? ''; if ($target_ip === '---') $target_ip = ''; if (!$message) exit(json_encode(['success' => false, 'error' => 'Empty message'])); // Robust IP/Session matching: if target is empty but user_id is set, try to find their last known details if (empty($target_ip) && empty($target_sid)) { if ($user_id != 0) { $stmt = db()->prepare("SELECT ip_address, session_id FROM messages WHERE user_id = ? AND ip_address != '' AND ip_address != '---' ORDER BY id DESC LIMIT 1"); $stmt->execute([$user_id]); $row = $stmt->fetch(); $target_ip = $row['ip_address'] ?? ''; $target_sid = $row['session_id'] ?? ''; } if (empty($target_ip)) { $target_ip = getRealIP(); } } $admin_id = $_SESSION['admin_id'] ?? 1; $sender = 'admin'; $stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?, ?)"); $stmt->execute([$user_id, $admin_id, $sender, $message, $target_ip, $target_sid]); $newId = db()->lastInsertId(); echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => $sender, 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]); exit; } if ($action === 'ping') { $user_id = $_SESSION['user_id'] ?? 0; $ip = getRealIP(); $sid = session_id(); $user_time = $_GET['user_time'] ?? ''; $stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, user_time = ?, session_id = VALUES(session_id)"); $stmt->execute([$user_id, $ip, $sid, $user_time, $user_time]); echo json_encode(['success' => true]); exit; } if ($action === 'admin_get_all') { header('Content-Type: application/json'); if (!isset($_SESSION['admin_id'])) { echo json_encode(['error' => 'Unauthorized']); exit; } try { // Robust query to get all active chat sessions, merging guest sessions into user sessions if IP or Session matches // Grouping by (final_user_id, effective_sid, effective_ip) to ensure "One ID, One Conversation" $stmt = db()->query(" SELECT v.final_user_id as user_id, v.effective_ip as ip_address, v.effective_sid as session_id, CASE WHEN m.message LIKE 'fetchAll(); // Convert unread_count to int foreach ($results as &$row) { $row['unread_count'] = (int)$row['unread_count']; } echo json_encode($results); } catch (Exception $e) { error_log("Chat API Error: " . $e->getMessage()); echo json_encode(['error' => $e->getMessage()]); } exit; } if ($action === 'save_remark') { if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized'])); $user_id = $_POST['user_id'] ?? 0; $ip = $_POST['ip_address'] ?? ''; $sid = $_POST['session_id'] ?? ''; if ($ip === '---') $ip = ''; $remark = $_POST['remark'] ?? ''; $stmt = db()->prepare("INSERT INTO chat_remarks (user_id, ip_address, session_id, remark) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE remark = ?, session_id = VALUES(session_id)"); $stmt->execute([$user_id, $ip, $sid, $remark, $remark]); echo json_encode(['success' => true]); exit; } if ($action === 'get_remark') { if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized'])); $user_id = $_GET['user_id'] ?? 0; $ip = $_GET['ip'] ?? ''; $sid = $_GET['session_id'] ?? ''; if ($ip === '---') $ip = ''; $stmt = db()->prepare("SELECT remark FROM chat_remarks WHERE user_id = ? AND (session_id = ? AND session_id != '') OR (user_id != 0 AND user_id = ?) OR (ip_address = ? AND ip_address != '' AND session_id = '')"); $stmt->execute([$user_id, $sid, $user_id, $ip]); $remark = $stmt->fetchColumn() ?: ''; echo json_encode(['success' => true, 'remark' => $remark]); exit; } if ($action === 'admin_delete_user') { if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized'])); $user_id = $_POST['user_id'] ?? 0; $ip = $_POST['ip_address'] ?? ''; $sid = $_POST['session_id'] ?? ''; if ($ip === '---') $ip = ''; if ($user_id != 0) { $stmt = db()->prepare("DELETE FROM messages WHERE user_id = ?"); $stmt->execute([$user_id]); $stmt = db()->prepare("DELETE FROM chat_visitors WHERE user_id = ?"); $stmt->execute([$user_id]); $stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = ?"); $stmt->execute([$user_id]); } else { $stmt = db()->prepare("DELETE FROM messages WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)"); $stmt->execute([$sid, $ip]); $stmt = db()->prepare("DELETE FROM chat_visitors WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)"); $stmt->execute([$sid, $ip]); $stmt = db()->prepare("DELETE FROM chat_remarks WHERE user_id = 0 AND (session_id = ? OR ip_address = ?)"); $stmt->execute([$sid, $ip]); } echo json_encode(['success' => true]); exit; } if ($action === 'admin_recall_message') { if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized'])); $message_id = $_POST['message_id'] ?? 0; $stmt = db()->prepare("DELETE FROM messages WHERE id = ? AND sender = 'admin'"); $stmt->execute([$message_id]); echo json_encode(['success' => true]); exit; }