更新部署

This commit is contained in:
Flatlogic Bot 2026-02-22 05:17:37 +00:00
parent 194fc7a771
commit 1adae4ff1b
5 changed files with 124 additions and 48 deletions

View File

@ -251,10 +251,6 @@ ob_start();
<div class="alert alert-info small border-0 bg-light text-primary"> <div class="alert alert-info small border-0 bg-light text-primary">
<i class="bi bi-info-circle-fill me-2"></i>填写后点击发送,前端充值弹窗将自动切换并显示此账户。 <i class="bi bi-info-circle-fill me-2"></i>填写后点击发送,前端充值弹窗将自动切换并显示此账户。
</div> </div>
<div class="mb-3">
<label class="form-label small fw-bold text-muted">充值金额 (Amount)</label>
<input type="number" id="pay-amount" class="form-control form-control-lg fs-6 fw-bold text-danger" placeholder="0.00" step="0.01">
</div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label small fw-bold text-muted">银行名称 / 支付方式 (Bank Name)</label> <label class="form-label small fw-bold text-muted">银行名称 / 支付方式 (Bank Name)</label>
<input type="text" id="pay-bank" class="form-control form-control-lg fs-6" placeholder="例如: 建设银行, Alipay, TRC20, etc."> <input type="text" id="pay-bank" class="form-control form-control-lg fs-6" placeholder="例如: 建设银行, Alipay, TRC20, etc.">
@ -392,12 +388,17 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
const jsName = username.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); const jsName = username.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
const jsRemark = rawRemark.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); const jsRemark = rawRemark.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
const unreadCount = parseInt(u.unread_count || 0);
html += ` html += `
<div class="user-card ${isActive ? 'active' : ''}" <div class="user-card ${isActive ? 'active' : ''}"
onclick="openChat('${userId}', '${rawIp}', '${rawSid}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')"> onclick="openChat('${userId}', '${rawIp}', '${rawSid}', '${jsName}', '${uid}', '${jsRemark}', '${userTime}', '${registrationIp}')">
<i class="bi bi-trash text-muted delete-chat-btn" title="删除会话" onclick="deleteChat('${userId}', '${rawIp}', '${rawSid}', event)"></i> <i class="bi bi-trash text-muted delete-chat-btn" title="删除会话" onclick="deleteChat('${userId}', '${rawIp}', '${rawSid}', event)"></i>
<div class="d-flex justify-content-between mb-1"> <div class="d-flex justify-content-between mb-1">
<span class="fw-bold small text-truncate" style="max-width: 150px;">${username}</span> <span class="fw-bold small text-truncate" style="max-width: 150px;">
${username}
${unreadCount > 0 ? `<span class="badge bg-danger rounded-pill ms-1" style="font-size: 9px; padding: 2px 5px;">${unreadCount}</span>` : ''}
</span>
<span class="text-muted" style="font-size: 10px;">${isNaN(lastTime.getTime()) ? '---' : lastTime.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'})}</span> <span class="text-muted" style="font-size: 10px;">${isNaN(lastTime.getTime()) ? '---' : lastTime.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'})}</span>
</div> </div>
${rawRemark ? `<div class="small text-danger text-truncate mb-1" style="font-size: 11px;">[备注: ${rawRemark}]</div>` : ''} ${rawRemark ? `<div class="small text-danger text-truncate mb-1" style="font-size: 11px;">[备注: ${rawRemark}]</div>` : ''}
@ -448,7 +449,13 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
lastMsgId = 0; lastMsgId = 0;
fetchMessages(); fetchMessages();
refreshUsers();
// Mark as read immediately when opening chat
const fd = new URLSearchParams();
fd.append('user_id', userId);
fd.append('ip_address', ip);
fd.append('session_id', sid);
fetch('/api/chat.php?action=mark_read', { method: 'POST', body: fd }).then(() => refreshUsers());
} }
async function recallMessage(msgId) { async function recallMessage(msgId) {
@ -617,7 +624,6 @@ async function notifyMatchSuccess() {
const bank = document.getElementById('pay-bank').value.trim(); const bank = document.getElementById('pay-bank').value.trim();
const name = document.getElementById('pay-name').value.trim(); const name = document.getElementById('pay-name').value.trim();
const account = document.getElementById('pay-account').value.trim(); const account = document.getElementById('pay-account').value.trim();
const amount = document.getElementById('pay-amount').value.trim();
if (!bank || !name || !account) { if (!bank || !name || !account) {
alert('请完整填写收款信息(银行、姓名、账号)'); alert('请完整填写收款信息(银行、姓名、账号)');
@ -629,7 +635,6 @@ async function notifyMatchSuccess() {
fd.append('bank', bank); fd.append('bank', bank);
fd.append('name', name); fd.append('name', name);
fd.append('account', account); fd.append('account', account);
if (amount) fd.append('amount', amount);
try { try {
const r = await fetch('/api/admin_recharge.php?action=match_success', { method: 'POST', body: fd }); const r = await fetch('/api/admin_recharge.php?action=match_success', { method: 'POST', body: fd });
@ -647,7 +652,6 @@ async function sendPaymentInfo() {
const bank = document.getElementById('pay-bank').value.trim(); const bank = document.getElementById('pay-bank').value.trim();
const name = document.getElementById('pay-name').value.trim(); const name = document.getElementById('pay-name').value.trim();
const account = document.getElementById('pay-account').value.trim(); const account = document.getElementById('pay-account').value.trim();
const amount = document.getElementById('pay-amount').value.trim();
if (!bank || !name || !account) { if (!bank || !name || !account) {
alert('请完整填写收款信息'); alert('请完整填写收款信息');
@ -659,10 +663,9 @@ async function sendPaymentInfo() {
fd.append('bank', bank); fd.append('bank', bank);
fd.append('name', name); fd.append('name', name);
fd.append('account', account); fd.append('account', account);
if (amount) fd.append('amount', amount);
try { try {
console.log('Sending account info...', { bank, name, account, amount }); console.log('Sending account info...', { bank, name, account });
const r = await fetch('/api/admin_recharge.php?action=send_account', { method: 'POST', body: fd }); const r = await fetch('/api/admin_recharge.php?action=send_account', { method: 'POST', body: fd });
const res = await r.json(); const res = await r.json();

View File

@ -123,6 +123,14 @@ function renderAdminPage($content, $title = '后台管理') {
.nav-link i { .nav-link i {
font-size: 18px; font-size: 18px;
} }
.badge-dot {
width: 8px;
height: 8px;
padding: 0;
border-radius: 50%;
background-color: #dc3545;
display: inline-block;
}
.card { .card {
border: none; border: none;
border-radius: 8px; border-radius: 8px;
@ -192,19 +200,19 @@ function renderAdminPage($content, $title = '后台管理') {
<!-- 7. 秒合约管理 --> <!-- 7. 秒合约管理 -->
<a href="/admin/binary.php" class="nav-link <?= $current_page == 'binary.php' ? 'active' : '' ?>"> <a href="/admin/binary.php" class="nav-link <?= $current_page == 'binary.php' ? 'active' : '' ?>">
<i class="bi bi-clock"></i> <?= __('sec_contract_management') ?> <i class="bi bi-clock"></i> <?= __('sec_contract_management') ?>
<span class="badge bg-info rounded-pill ms-auto d-none" id="binary-badge">0</span> <span class="badge bg-danger rounded-pill ms-auto d-none" id="binary-badge">0</span>
</a> </a>
<!-- 8. 现货管理 --> <!-- 8. 现货管理 -->
<a href="/admin/spot.php" class="nav-link <?= $current_page == 'spot.php' ? 'active' : '' ?>"> <a href="/admin/spot.php" class="nav-link <?= $current_page == 'spot.php' ? 'active' : '' ?>">
<i class="bi bi-currency-exchange"></i> <?= __('spot_trading') ?> <i class="bi bi-currency-exchange"></i> <?= __('spot_trading') ?>
<span class="badge bg-info rounded-pill ms-auto d-none" id="spot-badge">0</span> <span class="badge bg-danger rounded-pill ms-auto d-none" id="spot-badge">0</span>
</a> </a>
<!-- 9. 合约管理 --> <!-- 9. 合约管理 -->
<a href="/admin/contract.php" class="nav-link <?= $current_page == 'contract.php' ? 'active' : '' ?>"> <a href="/admin/contract.php" class="nav-link <?= $current_page == 'contract.php' ? 'active' : '' ?>">
<i class="bi bi-layers"></i> <?= __('contract_trading') ?> <i class="bi bi-layers"></i> <?= __('contract_trading') ?>
<span class="badge bg-info rounded-pill ms-auto d-none" id="contract-badge">0</span> <span class="badge bg-danger rounded-pill ms-auto d-none" id="contract-badge">0</span>
</a> </a>
<?php endif; ?> <?php endif; ?>
@ -227,7 +235,7 @@ function renderAdminPage($content, $title = '后台管理') {
<!-- 13. 在线客服 --> <!-- 13. 在线客服 -->
<a href="/admin/customer_service.php" class="nav-link <?= $current_page == 'customer_service.php' ? 'active' : '' ?>"> <a href="/admin/customer_service.php" class="nav-link <?= $current_page == 'customer_service.php' ? 'active' : '' ?>">
<i class="bi bi-headset"></i> <?= __('online_support') ?> <i class="bi bi-headset"></i> <?= __('online_support') ?>
<span class="badge bg-warning rounded-pill ms-auto d-none" id="messages-badge">0</span> <span class="badge bg-danger rounded-pill ms-auto d-none" id="messages-badge">0</span>
</a> </a>
<!-- 14. 后台设置 --> <!-- 14. 后台设置 -->
@ -456,8 +464,8 @@ function renderAdminPage($content, $title = '后台管理') {
.catch(e => console.error('Notification check failed:', e)); .catch(e => console.error('Notification check failed:', e));
} }
// Check every 10 seconds // Check every 2 seconds
setInterval(checkNotifications, 10000); setInterval(checkNotifications, 2000);
checkNotifications(); checkNotifications();
</script> </script>
<!-- Lightbox for Chat Images --> <!-- Lightbox for Chat Images -->

View File

@ -36,14 +36,6 @@ $active_contract = 0;
$new_messages = 0; $new_messages = 0;
$new_registrations = 0; $new_registrations = 0;
$cleared_recharge = $_SESSION['admin_cleared_finance'] ?? 0;
$cleared_kyc = $_SESSION['admin_cleared_kyc'] ?? 0;
$cleared_binary = $_SESSION['admin_cleared_binary'] ?? 0;
$cleared_spot = $_SESSION['admin_cleared_spot'] ?? 0;
$cleared_contract = $_SESSION['admin_cleared_contract'] ?? 0;
$cleared_messages = $_SESSION['admin_cleared_messages'] ?? 0;
$cleared_users = $_SESSION['admin_cleared_users'] ?? 0;
function getCount($db, $sql, $params) { function getCount($db, $sql, $params) {
$stmt = $db->prepare($sql); $stmt = $db->prepare($sql);
$stmt->execute($params); $stmt->execute($params);
@ -52,27 +44,27 @@ function getCount($db, $sql, $params) {
if ($admin['is_agent']) { if ($admin['is_agent']) {
$agent_id = $admin_id; $agent_id = $admin_id;
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'recharge' AND r.status NOT IN ('3', '4') AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]); $pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'recharge' AND r.status = '0' AND u.agent_id = ?", [$agent_id]);
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'withdrawal' AND r.status NOT IN ('3', '4') AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]); $pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'withdrawal' AND r.status = '0' AND u.agent_id = ?", [$agent_id]);
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_kyc]); $pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND agent_id = ?", [$agent_id]);
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_binary]); $active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND u.agent_id = ?", [$agent_id]);
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_spot]); $active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ?", [$agent_id]);
$active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'open' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_contract]); $active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'open' AND u.agent_id = ?", [$agent_id]);
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages m JOIN users u ON m.user_id = u.id WHERE m.sender = 'user' AND u.agent_id = ? AND UNIX_TIMESTAMP(m.created_at) > ?", [$agent_id, $cleared_messages]); $new_messages = getCount($db, "SELECT COUNT(*) FROM messages m JOIN users u ON m.user_id = u.id WHERE m.sender = 'user' AND m.is_read = 0 AND u.agent_id = ?", [$agent_id]);
$new_registrations = getCount($db, "SELECT COUNT(*) FROM users WHERE agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_users]); $new_registrations = getCount($db, "SELECT COUNT(*) FROM users WHERE agent_id = ? AND created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)", [$agent_id]);
} else { } else {
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'recharge' AND status NOT IN ('3', '4') AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]); $pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'recharge' AND status = '0'", []);
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'withdrawal' AND status NOT IN ('3', '4') AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]); $pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'withdrawal' AND status = '0'", []);
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_kyc]); $pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1", []);
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 'pending' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_binary]); $active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 'pending'", []);
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders WHERE status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_spot]); $active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders WHERE status = 0", []);
$active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders WHERE status = 'open' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_contract]); $active_contract = getCount($db, "SELECT COUNT(*) FROM contract_orders WHERE status = 'open'", []);
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages WHERE sender = 'user' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_messages]); $new_messages = getCount($db, "SELECT COUNT(*) FROM messages WHERE sender = 'user' AND is_read = 0", []);
$new_registrations = getCount($db, "SELECT COUNT(*) FROM users WHERE UNIX_TIMESTAMP(created_at) > ?", [$cleared_users]); $new_registrations = getCount($db, "SELECT COUNT(*) FROM users WHERE created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)", []);
} }
$total = $pending_recharge + $pending_withdrawal + $pending_kyc + $active_binary + $active_spot + $active_contract + $new_messages + $new_registrations; $total = $pending_recharge + $pending_withdrawal + $pending_kyc + $active_binary + $active_spot + $active_contract + $new_messages;
$sound_trigger_count = $pending_recharge + $pending_withdrawal + $new_messages + $new_registrations; $sound_trigger_count = $total; // Trigger sound for any pending action
echo json_encode([ echo json_encode([
'success' => true, 'success' => true,

View File

@ -107,6 +107,10 @@ if ($action === 'get_messages') {
if (isset($_SESSION['admin_id'])) { 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 = 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]); $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 { } else {
// User requesting their own messages // User requesting their own messages
$user_id = (int)($_SESSION['user_id'] ?? 0); $user_id = (int)($_SESSION['user_id'] ?? 0);
@ -157,6 +161,19 @@ if ($action === 'send_message') {
exit; 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 ($action === 'admin_send') {
if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized'])); if (!isset($_SESSION['admin_id'])) exit(json_encode(['success' => false, 'error' => 'Unauthorized']));
@ -237,7 +254,8 @@ if ($action === 'admin_get_all') {
SUBSTRING_INDEX(GROUP_CONCAT(effective_sid ORDER BY last_activity DESC SEPARATOR '|'), '|', 1) as effective_sid, SUBSTRING_INDEX(GROUP_CONCAT(effective_sid ORDER BY last_activity DESC SEPARATOR '|'), '|', 1) as effective_sid,
MAX(last_activity) as last_activity, MAX(last_activity) as last_activity,
MAX(user_time) as user_time, MAX(user_time) as user_time,
MAX(has_recharge) as has_recharge MAX(has_recharge) as has_recharge,
SUM(is_unread) as unread_count
FROM ( FROM (
SELECT SELECT
COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = m.ip_address AND m.ip_address != '---' AND m.ip_address != '' LIMIT 1), 0) as final_user_id, COALESCE(NULLIF(user_id, 0), (SELECT id FROM users WHERE registration_ip = m.ip_address AND m.ip_address != '---' AND m.ip_address != '' LIMIT 1), 0) as final_user_id,
@ -245,7 +263,8 @@ if ($action === 'admin_get_all') {
IFNULL(session_id, '') as effective_sid, IFNULL(session_id, '') as effective_sid,
created_at as last_activity, created_at as last_activity,
NULL as user_time, NULL as user_time,
0 as has_recharge 0 as has_recharge,
CASE WHEN sender = 'user' AND is_read = 0 THEN 1 ELSE 0 END as is_unread
FROM messages m FROM messages m
UNION ALL UNION ALL
SELECT SELECT
@ -254,7 +273,8 @@ if ($action === 'admin_get_all') {
IFNULL(session_id, '') as effective_sid, IFNULL(session_id, '') as effective_sid,
last_ping as last_activity, last_ping as last_activity,
user_time, user_time,
0 as has_recharge 0 as has_recharge,
0 as is_unread
FROM chat_visitors cv FROM chat_visitors cv
UNION ALL UNION ALL
SELECT SELECT
@ -263,7 +283,8 @@ if ($action === 'admin_get_all') {
'' as effective_sid, '' as effective_sid,
created_at as last_activity, created_at as last_activity,
NULL as user_time, NULL as user_time,
1 as has_recharge 1 as has_recharge,
0 as is_unread
FROM finance_requests fr FROM finance_requests fr
) t1 ) t1
GROUP BY final_user_id, (CASE WHEN final_user_id = 0 THEN effective_sid ELSE '' END), (CASE WHEN final_user_id = 0 AND effective_sid = '' THEN effective_ip ELSE '' END) GROUP BY final_user_id, (CASE WHEN final_user_id = 0 THEN effective_sid ELSE '' END), (CASE WHEN final_user_id = 0 AND effective_sid = '' THEN effective_ip ELSE '' END)
@ -284,7 +305,12 @@ if ($action === 'admin_get_all') {
LEFT JOIN chat_remarks r ON (v.final_user_id = r.user_id AND (v.final_user_id != 0 OR (v.effective_sid != '' AND v.effective_sid = r.session_id) OR (v.effective_sid = '' AND v.effective_ip = r.ip_address))) LEFT JOIN chat_remarks r ON (v.final_user_id = r.user_id AND (v.final_user_id != 0 OR (v.effective_sid != '' AND v.effective_sid = r.session_id) OR (v.effective_sid = '' AND v.effective_ip = r.ip_address)))
ORDER BY created_at DESC ORDER BY created_at DESC
"); ");
echo json_encode($stmt->fetchAll()); $results = $stmt->fetchAll();
// Convert unread_count to int
foreach ($results as &$row) {
$row['unread_count'] = (int)$row['unread_count'];
}
echo json_encode($results);
} catch (Exception $e) { } catch (Exception $e) {
error_log("Chat API Error: " . $e->getMessage()); error_log("Chat API Error: " . $e->getMessage());
echo json_encode(['error' => $e->getMessage()]); echo json_encode(['error' => $e->getMessage()]);

View File

@ -54,3 +54,50 @@ if (!function_exists('getSetting')) {
} }
} }
} }
// Ensure database schema is up to date (Automatic Migration)
function ensureSchema() {
try {
$db = db();
// --- finance_requests table ---
$stmt = $db->query("DESCRIBE finance_requests");
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
$finance_needed = [
'account_bank' => "VARCHAR(255) DEFAULT NULL",
'account_name' => "VARCHAR(255) DEFAULT NULL",
'account_number' => "VARCHAR(255) DEFAULT NULL",
'fiat_amount' => "DECIMAL(20,8) DEFAULT 0",
'fiat_currency' => "VARCHAR(10) DEFAULT NULL",
'ip_address' => "VARCHAR(45) DEFAULT NULL",
'payment_method' => "VARCHAR(100) DEFAULT NULL",
'payment_details' => "TEXT DEFAULT NULL"
];
foreach ($finance_needed as $col => $type) {
if (!in_array($col, $columns)) {
$db->exec("ALTER TABLE finance_requests ADD COLUMN $col $type");
}
}
// --- transactions table ---
$stmt = $db->query("DESCRIBE transactions");
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
$trans_needed = [
'ip_address' => "VARCHAR(45) DEFAULT NULL",
'status' => "VARCHAR(20) DEFAULT '0'"
];
foreach ($trans_needed as $col => $type) {
if (!in_array($col, $columns)) {
$db->exec("ALTER TABLE transactions ADD COLUMN $col $type");
}
}
} catch (Exception $e) {
// Silently fail or log to a file
}
}
ensureSchema();