mirror of
https://github.com/myronblair/tomtomgames-app
synced 2026-06-30 17:49:57 -05:00
967 lines
56 KiB
PHP
967 lines
56 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../../includes/auth.php';
|
|
header('Content-Type: application/json');
|
|
requireAdmin();
|
|
|
|
$action = $_GET['action'] ?? '';
|
|
|
|
switch ($action) {
|
|
|
|
// ─── STATS ────────────────────────────────────────────────
|
|
case 'stats':
|
|
echo json_encode(['success' => true, 'stats' => [
|
|
'total_users' => db()->query("SELECT COUNT(*) FROM users")->fetchColumn(),
|
|
'active_users' => db()->query("SELECT COUNT(*) FROM users WHERE status='active'")->fetchColumn(),
|
|
'pending_purchases' => db()->query("SELECT COUNT(*) FROM token_purchases WHERE status='pending'")->fetchColumn(),
|
|
'pending_cashouts' => db()->query("SELECT COUNT(*) FROM cashout_requests WHERE status='pending'")->fetchColumn(),
|
|
'pending_signups' => db()->query("SELECT COUNT(*) FROM pending_registrations WHERE expires_at > NOW()")->fetchColumn(),
|
|
'total_tokens_sold' => db()->query("SELECT COALESCE(SUM(tokens),0) FROM token_purchases WHERE status='completed'")->fetchColumn(),
|
|
'total_revenue' => db()->query("SELECT COALESCE(SUM(amount_cents),0)/100 FROM token_purchases WHERE status='completed'")->fetchColumn(),
|
|
]]);
|
|
break;
|
|
|
|
// ─── PENDING SIGNUPS ──────────────────────────────────────
|
|
case 'pending_signups':
|
|
$rows = db()->query("SELECT id,username,alias,email,expires_at,created_at FROM pending_registrations WHERE expires_at > NOW() ORDER BY created_at DESC")->fetchAll();
|
|
echo json_encode(['success'=>true,'pending'=>$rows]);
|
|
break;
|
|
|
|
case 'delete_pending':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($data['id'] ?? 0);
|
|
db()->prepare("DELETE FROM pending_registrations WHERE id=?")->execute([$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'approve_pending':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($data['id'] ?? 0);
|
|
// Fetch the pending record
|
|
$stmt = db()->prepare("SELECT * FROM pending_registrations WHERE id=?");
|
|
$stmt->execute([$id]);
|
|
$pending = $stmt->fetch();
|
|
if (!$pending) { echo json_encode(['success'=>false,'error'=>'Pending signup not found']); exit; }
|
|
// Check username/email not already taken
|
|
$chkUser = db()->prepare("SELECT id FROM users WHERE username=?");
|
|
$chkUser->execute([$pending['username']]);
|
|
if ($chkUser->fetch()) { echo json_encode(['success'=>false,'error'=>'Username already taken']); exit; }
|
|
if (!empty($pending['email'])) {
|
|
$chkEmail = db()->prepare("SELECT id FROM users WHERE email=?");
|
|
$chkEmail->execute([$pending['email']]);
|
|
if ($chkEmail->fetch()) { echo json_encode(['success'=>false,'error'=>'Email already registered']); exit; }
|
|
}
|
|
// Create the user account — bypass email verification
|
|
db()->beginTransaction();
|
|
try {
|
|
db()->prepare("INSERT INTO users (username,password,alias,email,email_verified,tokens,is_admin,status)
|
|
VALUES (?,?,?,?,1,0,0,'active')")
|
|
->execute([$pending['username'],$pending['password'],$pending['alias'],$pending['email']]);
|
|
db()->prepare("DELETE FROM pending_registrations WHERE id=?")->execute([$id]);
|
|
db()->commit();
|
|
logActivity('account_approved', (int)db()->lastInsertId(), (int)$_SESSION['user_id'], 'user', 0, 'Account approved for '.$pending['username']);
|
|
echo json_encode(['success'=>true,'username'=>$pending['username']]);
|
|
} catch (Exception $e) {
|
|
db()->rollBack();
|
|
echo json_encode(['success'=>false,'error'=>'Could not create account']);
|
|
}
|
|
break;
|
|
|
|
// ─── PURCHASES ────────────────────────────────────────────
|
|
case 'purchases':
|
|
$status = $_GET['status'] ?? 'pending';
|
|
if ($status === 'all') {
|
|
$stmt = db()->query("SELECT tp.*, u.username, u.alias FROM token_purchases tp JOIN users u ON tp.user_id=u.id ORDER BY tp.created_at DESC LIMIT 200");
|
|
} else {
|
|
$stmt = db()->prepare("SELECT tp.*, u.username, u.alias FROM token_purchases tp JOIN users u ON tp.user_id=u.id WHERE tp.status=? ORDER BY tp.created_at DESC LIMIT 200");
|
|
$stmt->execute([$status]);
|
|
}
|
|
echo json_encode(['success' => true, 'purchases' => $stmt->fetchAll()]);
|
|
break;
|
|
|
|
// ─── RESOLVE PURCHASE (approve manual / reject) ──────────
|
|
case 'resolve_purchase':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($data['id'] ?? 0);
|
|
$status = $data['status'] ?? '';
|
|
$note = trim($data['note'] ?? '');
|
|
|
|
if (!in_array($status, ['completed','failed'])) {
|
|
echo json_encode(['success'=>false,'error'=>'Invalid status']); exit;
|
|
}
|
|
|
|
// Fetch purchase
|
|
$row = db()->prepare("SELECT * FROM token_purchases WHERE id=? AND status='pending'");
|
|
$row->execute([$id]);
|
|
$purchase = $row->fetch();
|
|
|
|
if (!$purchase) {
|
|
echo json_encode(['success'=>false,'error'=>'Purchase not found or already resolved']); exit;
|
|
}
|
|
|
|
db()->beginTransaction();
|
|
try {
|
|
if ($status === 'completed') {
|
|
// Credit tokens to user
|
|
db()->prepare("logAdminAction('TOKENS_ADJUSTED', $adminId, 'user', isset($targetId)?$targetId:0, 'Manual token adjustment: '.($data['tokens']??0).' tokens', '', ($data['tokens']??''), 'critical');
|
|
db()->prepare("UPDATE users SET tokens=tokens+"? WHERE id=?")->execute([$purchase['tokens'], $purchase['user_id']]);
|
|
}
|
|
db()->prepare("UPDATE token_purchases SET status=?,admin_note=? WHERE id=?")->execute([$status, $note, $id]);
|
|
db()->commit();
|
|
echo json_encode(['success'=>true]);
|
|
} catch (Exception $e) {
|
|
db()->rollBack();
|
|
echo json_encode(['success'=>false,'error'=>'DB error']);
|
|
}
|
|
break;
|
|
|
|
// ─── CASHOUTS ─────────────────────────────────────────────
|
|
case 'cashouts':
|
|
$status = $_GET['status'] ?? 'pending';
|
|
$valid = ['pending','sent','approved','rejected','deleted'];
|
|
if (!in_array($status, $valid)) $status = 'pending';
|
|
if ($status === 'pending') {
|
|
// Show both pending (player editing) and locked (submitted to admin)
|
|
$stmt = db()->prepare("SELECT cr.*, u.username, u.alias AS user_alias FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.status IN ('pending','locked') ORDER BY cr.status DESC, cr.created_at DESC");
|
|
$stmt->execute();
|
|
} elseif ($status === 'sent') {
|
|
$stmt = db()->prepare("SELECT cr.*, u.username, u.alias AS user_alias FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.status IN ('sent','approved') ORDER BY cr.created_at DESC");
|
|
$stmt->execute();
|
|
} else {
|
|
$stmt = db()->prepare("SELECT cr.*, u.username, u.alias AS user_alias FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.status=? ORDER BY cr.created_at DESC");
|
|
$stmt->execute([$status]);
|
|
}
|
|
echo json_encode(['success'=>true,'cashouts'=>$stmt->fetchAll()]);
|
|
break;
|
|
|
|
case 'resolve_cashout':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($data['id'] ?? 0);
|
|
$status = $data['status'] ?? '';
|
|
$note = trim($data['note'] ?? '');
|
|
// 'approved' is treated as 'sent' (payment sent to player)
|
|
if ($status === 'approved') $status = 'sent';
|
|
if (!in_array($status, ['sent','rejected','deleted'])) { echo json_encode(['success'=>false,'error'=>'Invalid status']); exit; }
|
|
|
|
$r = db()->prepare("SELECT user_id,tokens FROM cashout_requests WHERE id=? AND status IN ('pending','locked')");
|
|
$r->execute([$id]);
|
|
$req = $r->fetch();
|
|
if (!$req) { echo json_encode(['success'=>false,'error'=>'Not found or already resolved']); exit; }
|
|
|
|
db()->beginTransaction();
|
|
try {
|
|
// Return tokens to player if denied or deleted
|
|
if (in_array($status, ['rejected','deleted'])) {
|
|
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$req['tokens'],$req['user_id']]);
|
|
}
|
|
db()->prepare("UPDATE cashout_requests SET status=?,admin_note=?,resolved_at=NOW() WHERE id=?")->execute([$status,$note,$id]);
|
|
db()->commit();
|
|
logActivity('cashout_'.$status, $req['user_id'], (int)$_SESSION['user_id'], 'cashout', $id,
|
|
'Cashout '.$status.' by admin. Tokens: '.$req['tokens'].($note?' Note: '.$note:''));
|
|
echo json_encode(['success'=>true,'status'=>$status]);
|
|
} catch (Exception $e) {
|
|
db()->rollBack();
|
|
echo json_encode(['success'=>false,'error'=>'DB error']);
|
|
}
|
|
break;
|
|
if (!in_array($status, ['approved','rejected'])) { echo json_encode(['success'=>false,'error'=>'Invalid status']); exit; }
|
|
|
|
if ($status === 'rejected') {
|
|
$r = db()->prepare("SELECT user_id,tokens FROM cashout_requests WHERE id=? AND status='pending'");
|
|
$r->execute([$id]);
|
|
$req = $r->fetch();
|
|
if ($req) db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$req['tokens'],$req['user_id']]);
|
|
}
|
|
db()->prepare("UPDATE cashout_requests SET status=?,admin_note=?,resolved_at=NOW() WHERE id=?")->execute([$status,$note,$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── USERS LIST ───────────────────────────────────────────
|
|
case 'users':
|
|
$users = db()->query("SELECT id,username,alias,email,email_verified,tokens,is_admin,status,created_at,last_login FROM users ORDER BY created_at DESC")->fetchAll();
|
|
echo json_encode(['success'=>true,'users'=>$users]);
|
|
break;
|
|
|
|
// ─── SINGLE USER DETAIL ───────────────────────────────────
|
|
case 'user_detail':
|
|
$uid = (int)($_GET['user_id'] ?? 0);
|
|
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
$stmt = db()->prepare("SELECT id,username,alias,email,email_verified,tokens,is_admin,status,created_at,last_login FROM users WHERE id=?");
|
|
$stmt->execute([$uid]);
|
|
$user = $stmt->fetch();
|
|
if (!$user) { echo json_encode(['success'=>false,'error'=>'User not found']); exit; }
|
|
// Rich stats
|
|
$s1 = db()->prepare("SELECT COALESCE(SUM(amount_cents),0)/100 FROM token_purchases WHERE user_id=? AND status='completed'");
|
|
$s1->execute([$uid]);
|
|
$s2 = db()->prepare("SELECT COUNT(*) FROM token_purchases WHERE user_id=? AND status='completed'"); $s2->execute([$uid]);
|
|
$s3 = db()->prepare("SELECT COUNT(*) FROM token_purchases WHERE user_id=? AND status='pending'"); $s3->execute([$uid]);
|
|
$s4 = db()->prepare("SELECT COUNT(*) FROM token_purchases WHERE user_id=? AND status='failed'"); $s4->execute([$uid]);
|
|
$s5 = db()->prepare("SELECT COUNT(*) FROM cashout_requests WHERE user_id=?"); $s5->execute([$uid]);
|
|
$s6 = db()->prepare("SELECT COALESCE(SUM(tokens),0) FROM token_purchases WHERE user_id=? AND status='completed'"); $s6->execute([$uid]);
|
|
$stats = [
|
|
'total_spent' => $s1->fetchColumn(),
|
|
'completed_purchases'=> $s2->fetchColumn(),
|
|
'pending_purchases' => $s3->fetchColumn(),
|
|
'failed_purchases' => $s4->fetchColumn(),
|
|
'total_cashouts' => $s5->fetchColumn(),
|
|
'total_tokens_bought'=> $s6->fetchColumn(),
|
|
];
|
|
echo json_encode(['success'=>true,'user'=>$user,'stats'=>$stats]);
|
|
break;
|
|
|
|
// ─── USER PURCHASES ───────────────────────────────────────
|
|
case 'user_purchases':
|
|
$uid = (int)($_GET['user_id'] ?? 0);
|
|
$stmt = db()->prepare("SELECT id,tokens,amount_cents,payment_method,square_payment_id,platform_id,game_alias,player_name,billing_name,billing_address,billing_city,billing_state,billing_zip,billing_email,is_custom,failure_reason,card_brand,card_last4,receipt_url,status,admin_note,created_at FROM token_purchases WHERE user_id=? ORDER BY created_at DESC LIMIT 100");
|
|
$stmt->execute([$uid]);
|
|
echo json_encode(['success'=>true,'purchases'=>$stmt->fetchAll()]);
|
|
break;
|
|
|
|
// ─── USER CASHOUTS ────────────────────────────────────────
|
|
case 'user_cashouts':
|
|
$uid = (int)($_GET['user_id'] ?? 0);
|
|
$stmt = db()->prepare("SELECT * FROM cashout_requests WHERE user_id=? ORDER BY created_at DESC LIMIT 100");
|
|
$stmt->execute([$uid]);
|
|
echo json_encode(['success'=>true,'cashouts'=>$stmt->fetchAll()]);
|
|
break;
|
|
|
|
// ─── ADJUST TOKENS ────────────────────────────────────────
|
|
case 'adjust_tokens':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
$amount = (float)($data['amount'] ?? 0);
|
|
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$amount,$uid]);
|
|
$bal = db()->prepare("SELECT tokens FROM users WHERE id=?"); $bal->execute([$uid]);
|
|
$newBal = $bal->fetchColumn();
|
|
echo json_encode(['success'=>true,'new_balance'=>$newBal]);
|
|
break;
|
|
|
|
// ─── SET EXACT TOKEN BALANCE ──────────────────────────────
|
|
case 'set_tokens':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
$bal = (float)($data['balance'] ?? 0);
|
|
if ($bal < 0) { echo json_encode(['success'=>false,'error'=>'Balance cannot be negative']); exit; }
|
|
db()->prepare("UPDATE users SET tokens=? WHERE id=?")->execute([$bal,$uid]);
|
|
echo json_encode(['success'=>true,'new_balance'=>$bal]);
|
|
break;
|
|
|
|
// ─── EDIT USER ────────────────────────────────────────────
|
|
case 'edit_user':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
$username = strtolower(trim($data['username'] ?? ''));
|
|
$alias = trim($data['alias'] ?? '');
|
|
$email = strtolower(trim($data['email'] ?? ''));
|
|
$password = trim($data['password'] ?? '');
|
|
|
|
if (!$uid || empty($username) || empty($alias))
|
|
{ echo json_encode(['success'=>false,'error'=>'Username and alias required']); exit; }
|
|
if (!preg_match('/^[a-z0-9_]{3,50}$/', $username))
|
|
{ echo json_encode(['success'=>false,'error'=>'Invalid username format']); exit; }
|
|
if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL))
|
|
{ echo json_encode(['success'=>false,'error'=>'Invalid email address']); exit; }
|
|
|
|
// Check username not taken by another user
|
|
$chk = db()->prepare("SELECT id FROM users WHERE username=? AND id!=?");
|
|
$chk->execute([$username,$uid]);
|
|
if ($chk->fetch()) { echo json_encode(['success'=>false,'error'=>'Username already taken']); exit; }
|
|
|
|
if (!empty($email)) {
|
|
$chk2 = db()->prepare("SELECT id FROM users WHERE email=? AND id!=?");
|
|
$chk2->execute([$email,$uid]);
|
|
if ($chk2->fetch()) { echo json_encode(['success'=>false,'error'=>'Email already in use']); exit; }
|
|
}
|
|
|
|
if (!empty($password)) {
|
|
if (strlen($password) < 6) { echo json_encode(['success'=>false,'error'=>'Password must be 6+ characters']); exit; }
|
|
$hash = password_hash($password, PASSWORD_BCRYPT);
|
|
db()->prepare("UPDATE users SET username=?,alias=?,email=?,password=? WHERE id=?")->execute([$username,$alias,$email,$hash,$uid]);
|
|
} else {
|
|
db()->prepare("UPDATE users SET username=?,alias=?,email=? WHERE id=?")->execute([$username,$alias,$email,$uid]);
|
|
}
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── TOGGLE ADMIN ROLE ───────────────────────────────────
|
|
case 'toggle_admin':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
// Master admin (ID=1) can NEVER lose admin status
|
|
if ($uid === MASTER_ADMIN_ID) {
|
|
echo json_encode(['success'=>false,'error'=>'Master admin cannot be modified.']); exit;
|
|
}
|
|
// Cannot remove your own admin
|
|
if ($uid === (int)$_SESSION['user_id']) {
|
|
echo json_encode(['success'=>false,'error'=>'You cannot change your own admin status.']); exit;
|
|
}
|
|
// Only master admin can grant/revoke admin
|
|
if ((int)$_SESSION['user_id'] !== MASTER_ADMIN_ID) {
|
|
echo json_encode(['success'=>false,'error'=>'Only the master admin can change admin roles.']); exit;
|
|
}
|
|
$stmt = db()->prepare("SELECT is_admin FROM users WHERE id=?");
|
|
$stmt->execute([$uid]);
|
|
$current = $stmt->fetchColumn();
|
|
$new_val = $current ? 0 : 1;
|
|
// If granting admin, also set email_verified=1
|
|
if ($new_val) {
|
|
db()->prepare("UPDATE users SET is_admin=1, email_verified=1 WHERE id=?")->execute([$uid]);
|
|
} else {
|
|
db()->prepare("UPDATE users SET is_admin=0 WHERE id=?")->execute([$uid]);
|
|
}
|
|
logActivity($new_val?'admin_granted':'admin_revoked', $uid, (int)$_SESSION['user_id'], 'user', $uid, 'Admin status changed to '.($new_val?'admin':'player'));
|
|
echo json_encode(['success'=>true, 'is_admin'=>$new_val]);
|
|
break;
|
|
|
|
// ─── TOGGLE SUSPEND ───────────────────────────────────────
|
|
case 'toggle_user':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
if ($uid === MASTER_ADMIN_ID) { echo json_encode(['success'=>false,'error'=>'Cannot suspend the master admin.']); exit; }
|
|
db()->prepare("logAdminAction('USER_STATUS_CHANGE', $adminId, 'user', isset($userId)?$userId:0, 'Changed user status to: '.($data['status']??'unknown'), '', ($data['status']??''), 'warning');
|
|
db()->prepare("UPDATE users SET status="IF(status='active','suspended','active') WHERE id=?")->execute([$uid]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── DELETE USER ──────────────────────────────────────────
|
|
case 'delete_user':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
if (!$uid) { echo json_encode(['success'=>false,'error'=>'Invalid user']); exit; }
|
|
// Prevent deleting own account
|
|
if ($uid === MASTER_ADMIN_ID) { echo json_encode(['success'=>false,'error'=>'Cannot delete the master admin account.']); exit; }
|
|
if ($uid === (int)$_SESSION['user_id']) { echo json_encode(['success'=>false,'error'=>'Cannot delete your own account']); exit; }
|
|
db()->beginTransaction();
|
|
try {
|
|
db()->prepare("DELETE FROM chat_messages WHERE user_id=?")->execute([$uid]);
|
|
db()->prepare("DELETE FROM cashout_requests WHERE user_id=?")->execute([$uid]);
|
|
db()->prepare("DELETE FROM token_purchases WHERE user_id=?")->execute([$uid]);
|
|
db()->prepare("DELETE FROM users WHERE id=?")->execute([$uid]);
|
|
db()->commit();
|
|
echo json_encode(['success'=>true]);
|
|
} catch (Exception $e) {
|
|
db()->rollBack();
|
|
echo json_encode(['success'=>false,'error'=>'Delete failed']);
|
|
}
|
|
break;
|
|
|
|
// ─── SEND PASSWORD RESET ──────────────────────────────────
|
|
case 'send_password_reset':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
$stmt = db()->prepare("SELECT email,alias FROM users WHERE id=?");
|
|
$stmt->execute([$uid]);
|
|
$user = $stmt->fetch();
|
|
if (!$user || empty($user['email'])) { echo json_encode(['success'=>false,'error'=>'No email on file']); exit; }
|
|
// Generate reset token — reuse pending_registrations pattern
|
|
$token = bin2hex(random_bytes(32));
|
|
$exp = date('Y-m-d H:i:s', time() + 3600); // 1 hour
|
|
db()->prepare("INSERT INTO pending_registrations (username,password,alias,email,token,expires_at) VALUES ('__reset__','',''.?,?,'__reset__',?) ON DUPLICATE KEY UPDATE token=VALUES(token),expires_at=VALUES(expires_at)")->execute([$user['alias'],$user['email'],$token,$exp]);
|
|
// Simple reset email
|
|
$resetUrl = rtrim(SITE_URL,'/') . '/reset_password.php?token=' . urlencode($token);
|
|
$subject = SITE_NAME . ' — Password Reset Request';
|
|
$body = "Hi {$user['alias']},\n\nA password reset was requested for your account.\n\nClick here to reset: {$resetUrl}\n\nExpires in 1 hour. If you didn't request this, ignore this email.\n\n— " . SITE_NAME;
|
|
$headers = "From: " . MAIL_FROM_NAME . " <" . MAIL_FROM . ">\r\nReply-To: " . MAIL_REPLY_TO;
|
|
mail($user['email'], $subject, $body, $headers, '-f' . MAIL_FROM);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── PLATFORM ACCOUNTS ────────────────────────────────
|
|
case 'platform_accounts_list':
|
|
$status = $_GET['status'] ?? 'pending';
|
|
$uid = (int)($_GET['user_id'] ?? 0);
|
|
if ($uid) {
|
|
$stmt = db()->prepare("SELECT pa.*, COALESCE(p.name,pa.platform_slug) AS platform_name, u.username, u.alias AS user_alias FROM platform_accounts pa LEFT JOIN platforms p ON pa.platform_slug=p.slug JOIN users u ON pa.user_id=u.id WHERE pa.user_id=? ORDER BY pa.requested_at DESC");
|
|
$stmt->execute([$uid]);
|
|
} else {
|
|
$valid = ['pending','approved','denied','deleted'];
|
|
if (!in_array($status,$valid)) $status='pending';
|
|
$stmt = db()->prepare("SELECT pa.*, COALESCE(p.name,pa.platform_slug) AS platform_name, u.username, u.alias AS user_alias FROM platform_accounts pa LEFT JOIN platforms p ON pa.platform_slug=p.slug JOIN users u ON pa.user_id=u.id WHERE pa.status=? ORDER BY pa.requested_at DESC");
|
|
$stmt->execute([$status]);
|
|
}
|
|
echo json_encode(['success'=>true,'accounts'=>$stmt->fetchAll()]);
|
|
break;
|
|
|
|
case 'platform_account_resolve':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$id=$d['id']??0; $status=$d['status']??'';
|
|
$uname=substr(trim($d['platform_username']??''),0,100);
|
|
$pass=substr(trim($d['platform_password']??''),0,200);
|
|
$note=substr(trim($d['admin_note']??''),0,300);
|
|
if (!in_array($status,['approved','denied','deleted'])){echo json_encode(['success'=>false,'error'=>'Invalid status']);exit;}
|
|
$chk=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$chk->execute([$id]);$row=$chk->fetch();
|
|
if (!$row){echo json_encode(['success'=>false,'error'=>'Not found']);exit;}
|
|
db()->prepare("UPDATE platform_accounts SET status=?,platform_username=?,platform_password=?,admin_note=?,resolved_at=NOW(),admin_id=? WHERE id=?")
|
|
->execute([$status,$uname,$pass,$note,(int)$_SESSION['user_id'],$id]);
|
|
if ($status==='approved'&&$uname) {
|
|
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")
|
|
->execute([$row['user_id'],$row['platform_slug'],$uname]);
|
|
}
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'platform_account_update':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d=json_decode(file_get_contents('php://input'),true);
|
|
$id=$d['id']??0;
|
|
$uname=substr(trim($d['platform_username']??''),0,100);
|
|
$pass=substr(trim($d['platform_password']??''),0,200);
|
|
$note=substr(trim($d['admin_note']??''),0,300);
|
|
$chk=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$chk->execute([$id]);$row=$chk->fetch();
|
|
if (!$row){echo json_encode(['success'=>false,'error'=>'Not found']);exit;}
|
|
db()->prepare("UPDATE platform_accounts SET platform_username=?,platform_password=?,admin_note=? WHERE id=?")
|
|
->execute([$uname,$pass,$note,$id]);
|
|
if ($uname){
|
|
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")
|
|
->execute([$row['user_id'],$row['platform_slug'],$uname]);
|
|
}
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
$rows = db()->query("
|
|
SELECT b.*, u.username AS sender_name,
|
|
(SELECT COUNT(*) FROM broadcast_reads WHERE broadcast_id=b.id) AS read_count,
|
|
(SELECT COUNT(*) FROM broadcast_replies WHERE broadcast_id=b.id) AS reply_count,
|
|
(SELECT COUNT(*) FROM users WHERE is_admin=0 AND status='active') AS total_players
|
|
FROM broadcasts b JOIN users u ON b.admin_id=u.id
|
|
ORDER BY b.sent_at DESC LIMIT 50
|
|
")->fetchAll();
|
|
echo json_encode(['success'=>true,'broadcasts'=>$rows]);
|
|
break;
|
|
|
|
case 'broadcast_send':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$subject = substr(trim($d['subject']??''),0,200);
|
|
$message = substr(trim($d['message']??''),0,5000);
|
|
$target = in_array($d['target']??'',['all','verified','unverified','admins']) ? $d['target'] : 'all';
|
|
if (!$subject||!$message) { echo json_encode(['success'=>false,'error'=>'Subject and message required']); exit; }
|
|
db()->prepare("INSERT INTO broadcasts (admin_id,subject,message,target) VALUES (?,?,?,?)")
|
|
->execute([$_SESSION['user_id'],$subject,$message,$target]);
|
|
$bid = db()->lastInsertId();
|
|
// Count recipients
|
|
$countQ = [
|
|
'all' => "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0",
|
|
'verified' => "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0 AND email_verified=1",
|
|
'unverified' => "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0 AND email_verified=0",
|
|
'admins' => "SELECT COUNT(*) FROM users WHERE is_admin=1",
|
|
];
|
|
$count = db()->query($countQ[$target])->fetchColumn();
|
|
echo json_encode(['success'=>true,'id'=>$bid,'recipient_count'=>(int)$count]);
|
|
break;
|
|
|
|
case 'broadcast_delete':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($d['id']??0);
|
|
db()->prepare("DELETE FROM broadcasts WHERE id=?")->execute([$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'broadcast_reads':
|
|
$bid = (int)($_GET['broadcast_id']??0);
|
|
$rows = db()->prepare("SELECT br.read_at, u.username, u.alias FROM broadcast_reads br JOIN users u ON br.user_id=u.id WHERE br.broadcast_id=? ORDER BY br.read_at ASC");
|
|
$rows->execute([$bid]);
|
|
echo json_encode(['success'=>true,'reads'=>$rows->fetchAll()]);
|
|
break;
|
|
|
|
case 'broadcast_replies':
|
|
$bid = (int)($_GET['broadcast_id']??0);
|
|
$rows = db()->prepare("SELECT br.*, u.username, u.alias, u.is_admin FROM broadcast_replies br JOIN users u ON br.user_id=u.id WHERE br.broadcast_id=? ORDER BY br.created_at ASC");
|
|
$rows->execute([$bid]);
|
|
echo json_encode(['success'=>true,'replies'=>$rows->fetchAll()]);
|
|
break;
|
|
|
|
case 'broadcast_reply':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$bid = (int)($d['broadcast_id']??0);
|
|
$msg = substr(trim($d['message']??''),0,1000);
|
|
if (!$bid||!$msg) { echo json_encode(['success'=>false,'error'=>'Required fields missing']); exit; }
|
|
db()->prepare("INSERT INTO broadcast_replies (broadcast_id,user_id,message) VALUES (?,?,?)")
|
|
->execute([$bid,$_SESSION['user_id'],$msg]);
|
|
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
|
break;
|
|
$rows = db()->query("SELECT * FROM cashout_method_types ORDER BY sort_order ASC, id ASC")->fetchAll();
|
|
echo json_encode(['success'=>true,'types'=>$rows]);
|
|
break;
|
|
|
|
case 'cashout_methods_create':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$slug = preg_replace('/[^a-z0-9_]/','',strtolower(trim($d['slug']??'')));
|
|
$label= substr(trim($d['label']??''),0,100);
|
|
$icon = substr(trim($d['icon']??'💰'),0,10);
|
|
$desc = substr(trim($d['description']??''),0,200);
|
|
$sort = (int)($d['sort_order']??99);
|
|
$active=(int)(bool)($d['is_active']??1);
|
|
if (!$slug||!$label){echo json_encode(['success'=>false,'error'=>'Slug and label required']);exit;}
|
|
try {
|
|
db()->prepare("INSERT INTO cashout_method_types (slug,label,icon,description,is_active,sort_order) VALUES (?,?,?,?,?,?)")
|
|
->execute([$slug,$label,$icon,$desc,$active,$sort]);
|
|
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
|
} catch(Exception $e){ echo json_encode(['success'=>false,'error'=>'Slug already exists']); }
|
|
break;
|
|
|
|
case 'cashout_methods_update':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($d['id']??0);
|
|
$label= substr(trim($d['label']??''),0,100);
|
|
$icon = substr(trim($d['icon']??'💰'),0,10);
|
|
$desc = substr(trim($d['description']??''),0,200);
|
|
$sort = (int)($d['sort_order']??0);
|
|
$active=(int)(bool)($d['is_active']??1);
|
|
if (!$id||!$label){echo json_encode(['success'=>false,'error'=>'ID and label required']);exit;}
|
|
db()->prepare("UPDATE cashout_method_types SET label=?,icon=?,description=?,is_active=?,sort_order=? WHERE id=?")
|
|
->execute([$label,$icon,$desc,$active,$sort,$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'cashout_methods_delete':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d=(json_decode(file_get_contents('php://input'),true));
|
|
$id=(int)($d['id']??0);
|
|
if (!$id){echo json_encode(['success'=>false,'error'=>'ID required']);exit;}
|
|
db()->prepare("DELETE FROM cashout_method_types WHERE id=?")->execute([$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── PLATFORM ACCOUNTS ────────────────────────────────
|
|
case 'platform_accounts_list':
|
|
$status = $_GET['status'] ?? 'pending';
|
|
$valid = ['pending','approved','denied','deleted'];
|
|
if (!in_array($status,$valid)) $status='pending';
|
|
$stmt = db()->prepare("
|
|
SELECT pa.*, u.username, u.alias,
|
|
COALESCE(p.name, pa.platform_name, pa.platform_slug) AS display_name,
|
|
p.color
|
|
FROM platform_accounts pa
|
|
JOIN users u ON pa.user_id = u.id
|
|
LEFT JOIN platforms p ON pa.platform_slug = p.slug
|
|
WHERE pa.status = ?
|
|
ORDER BY pa.requested_at DESC
|
|
");
|
|
$stmt->execute([$status]);
|
|
echo json_encode(['success'=>true,'accounts'=>$stmt->fetchAll()]);
|
|
break;
|
|
|
|
case 'platform_account_approve':
|
|
if ($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['success'=>false]);exit;}
|
|
$d = json_decode(file_get_contents('php://input'),true);
|
|
$id = (int)($d['id']??0);
|
|
$u = substr(trim($d['provided_username']??''),0,100);
|
|
$pw = substr(trim($d['provided_password']??''),0,200);
|
|
$nt = substr(trim($d['admin_note']??''),0,500);
|
|
if (!$id||!$u||!$pw){echo json_encode(['success'=>false,'error'=>'ID, username and password required']);exit;}
|
|
$r=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$r->execute([$id]);$req=$r->fetch();
|
|
if(!$req){echo json_encode(['success'=>false,'error'=>'Not found']);exit;}
|
|
db()->prepare("UPDATE platform_accounts SET status='approved',provided_username=?,provided_password=?,admin_note=?,approved_at=NOW(),admin_id=? WHERE id=?")
|
|
->execute([$u,$pw,$nt,$_SESSION['user_id'],$id]);
|
|
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")
|
|
->execute([$req['user_id'],$req['platform_slug'],$u]);
|
|
try{logActivity('platform_account_approved',$req['user_id'],(int)$_SESSION['user_id'],'platform_account',$id,"Approved {$req['platform_slug']}: {$u}");}catch(Exception $e){}
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'platform_account_update':
|
|
if ($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['success'=>false]);exit;}
|
|
$d = json_decode(file_get_contents('php://input'),true);
|
|
$id = (int)($d['id']??0);
|
|
$u = substr(trim($d['provided_username']??''),0,100);
|
|
$pw = substr(trim($d['provided_password']??''),0,200);
|
|
$nt = substr(trim($d['admin_note']??''),0,500);
|
|
if (!$id){echo json_encode(['success'=>false,'error'=>'ID required']);exit;}
|
|
db()->prepare("UPDATE platform_accounts SET provided_username=?,provided_password=?,admin_note=?,admin_id=? WHERE id=?")
|
|
->execute([$u,$pw,$nt,$_SESSION['user_id'],$id]);
|
|
$r=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$r->execute([$id]);$req=$r->fetch();
|
|
if($req&&$u){db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")->execute([$req['user_id'],$req['platform_slug'],$u]);}
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'platform_account_deny':
|
|
if ($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['success'=>false]);exit;}
|
|
$d=json_decode(file_get_contents('php://input'),true);
|
|
$id=(int)($d['id']??0);$nt=substr(trim($d['admin_note']??''),0,500);
|
|
db()->prepare("UPDATE platform_accounts SET status='denied',admin_note=?,admin_id=? WHERE id=?")->execute([$nt,$_SESSION['user_id'],$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'platform_account_delete':
|
|
if ($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['success'=>false]);exit;}
|
|
$d=json_decode(file_get_contents('php://input'),true);
|
|
$id=(int)($d['id']??0);
|
|
db()->prepare("DELETE FROM platform_accounts WHERE id=?")->execute([$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
case 'platform_accounts_user':
|
|
$uid=(int)($_GET['user_id']??0);
|
|
$stmt=db()->prepare("SELECT pa.*,COALESCE(p.name,pa.platform_name,pa.platform_slug) AS display_name,p.color,p.player_url FROM platform_accounts pa LEFT JOIN platforms p ON pa.platform_slug=p.slug WHERE pa.user_id=? ORDER BY pa.requested_at DESC");
|
|
$stmt->execute([$uid]);
|
|
echo json_encode(['success'=>true,'accounts'=>$stmt->fetchAll()]);
|
|
break;
|
|
case 'activity_log':
|
|
case 'activity_log_v2':
|
|
$page = max(1, (int)($_GET['page']??1));
|
|
$limit = 20;
|
|
$offset = ($page - 1) * $limit;
|
|
$category = trim($_GET['category'] ?? '');
|
|
$severity = trim($_GET['severity'] ?? '');
|
|
$search = trim($_GET['search'] ?? '');
|
|
$date = trim($_GET['date'] ?? '');
|
|
|
|
$where = ["al.created_at >= DATE_SUB(NOW(), INTERVAL 90 DAY)"];
|
|
$params = [];
|
|
|
|
if ($category) { $where[] = "al.category = ?"; $params[] = $category; }
|
|
if ($severity) { $where[] = "al.severity = ?"; $params[] = $severity; }
|
|
if ($date) { $where[] = "DATE(al.created_at) = ?"; $params[] = $date; }
|
|
if ($search) {
|
|
$where[] = "(al.action LIKE ? OR al.detail LIKE ? OR u.username LIKE ? OR u.alias LIKE ? OR al.ip LIKE ?)";
|
|
$s = '%'.$search.'%';
|
|
$params = array_merge($params, [$s,$s,$s,$s,$s]);
|
|
}
|
|
|
|
$whereStr = implode(' AND ', $where);
|
|
$baseQuery = "FROM activity_log al
|
|
LEFT JOIN users u ON al.user_id = u.id
|
|
LEFT JOIN users a ON al.admin_id = a.id
|
|
WHERE $whereStr";
|
|
|
|
$countStmt = db()->prepare("SELECT COUNT(*) $baseQuery");
|
|
$countStmt->execute($params);
|
|
$total = (int)$countStmt->fetchColumn();
|
|
|
|
$dataStmt = db()->prepare("SELECT al.*, u.username, u.alias,
|
|
a.username AS admin_username
|
|
$baseQuery
|
|
ORDER BY al.created_at DESC
|
|
LIMIT $limit OFFSET $offset");
|
|
$dataStmt->execute($params);
|
|
$events = $dataStmt->fetchAll();
|
|
|
|
// Stats for the current filter set
|
|
$statsParams = $params;
|
|
$statsStmt = db()->prepare("SELECT
|
|
SUM(al.severity='critical') AS critical,
|
|
SUM(al.severity='warning') AS warning,
|
|
COUNT(DISTINCT al.ip) AS unique_ips
|
|
$baseQuery");
|
|
$statsStmt->execute($statsParams);
|
|
$stats = $statsStmt->fetch();
|
|
|
|
echo json_encode(['success'=>true,'events'=>$events,'total'=>$total,'page'=>$page,'stats'=>$stats]);
|
|
break;
|
|
|
|
case 'activity_log_csv':
|
|
$category = trim($_GET['category'] ?? '');
|
|
$severity = trim($_GET['severity'] ?? '');
|
|
$search = trim($_GET['search'] ?? '');
|
|
$date = trim($_GET['date'] ?? '');
|
|
|
|
$where = ["al.created_at >= DATE_SUB(NOW(), INTERVAL 90 DAY)"];
|
|
$params = [];
|
|
if ($category) { $where[] = "al.category = ?"; $params[] = $category; }
|
|
if ($severity) { $where[] = "al.severity = ?"; $params[] = $severity; }
|
|
if ($date) { $where[] = "DATE(al.created_at) = ?"; $params[] = $date; }
|
|
if ($search) {
|
|
$where[] = "(al.action LIKE ? OR al.detail LIKE ? OR u.username LIKE ?)";
|
|
$s = '%'.$search.'%'; $params = array_merge($params, [$s,$s,$s]);
|
|
}
|
|
|
|
$whereStr = implode(' AND ', $where);
|
|
$stmt = db()->prepare("SELECT al.*, u.username, u.alias, a.username AS admin_username
|
|
FROM activity_log al
|
|
LEFT JOIN users u ON al.user_id=u.id
|
|
LEFT JOIN users a ON al.admin_id=a.id
|
|
WHERE $whereStr ORDER BY al.created_at DESC LIMIT 5000");
|
|
$stmt->execute($params);
|
|
$rows = $stmt->fetchAll();
|
|
|
|
header('Content-Type: text/csv');
|
|
header('Content-Disposition: attachment; filename="tomtomgames_audit_' . date('Y-m-d') . '.csv"');
|
|
$out = fopen('php://output', 'w');
|
|
fputcsv($out, ['ID','Timestamp','Category','Severity','Action','Username','Alias','Admin','Detail','Old Value','New Value','IP','User Agent','Page','Session ID']);
|
|
foreach ($rows as $r) {
|
|
fputcsv($out, [$r['id'],$r['created_at'],$r['category'],$r['severity'],$r['action'],
|
|
$r['username']??'',$r['alias']??'',$r['admin_username']??'',
|
|
$r['detail']??'',$r['old_value']??'',$r['new_value']??'',
|
|
$r['ip']??'',$r['user_agent']??'',$r['page']??'',$r['session_id']??'']);
|
|
}
|
|
fclose($out);
|
|
exit;
|
|
break;
|
|
|
|
// ─── CASHOUT METHODS: list (admin) ────────────────────
|
|
case 'cashout_methods_list':
|
|
$rows = db()->query("SELECT * FROM cashout_method_types ORDER BY sort_order ASC, id ASC")->fetchAll();
|
|
echo json_encode(['success'=>true,'types'=>$rows]);
|
|
break;
|
|
|
|
// ─── PAYMENT SETTINGS: list (admin) ───────────────────
|
|
case 'payment_settings_list':
|
|
$rows = db()->query("SELECT * FROM payment_settings ORDER BY sort_order ASC, id ASC")->fetchAll();
|
|
echo json_encode(['success'=>true,'methods'=>$rows]);
|
|
break;
|
|
|
|
case 'payment_settings_update':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($d['id'] ?? 0);
|
|
$label= substr(trim($d['label']??''), 0, 100);
|
|
$handle = substr(trim($d['handle']??''), 0, 200);
|
|
$inst = substr(trim($d['instructions']??''), 0, 500);
|
|
$enabled = (int)(bool)($d['is_enabled'] ?? 1);
|
|
$sort = (int)($d['sort_order'] ?? 0);
|
|
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
|
db()->prepare("UPDATE payment_settings SET label=?,handle=?,instructions=?,is_enabled=?,sort_order=? WHERE id=?")
|
|
->execute([$label,$handle,$inst,$enabled,$sort,$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
|
|
// ─── PAYOUT METHODS: get for user ────────────────────────
|
|
case 'payout_methods_get':
|
|
$uid = (int)($_GET['user_id'] ?? 0);
|
|
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
$rows = db()->prepare("SELECT * FROM payout_methods WHERE user_id=? ORDER BY is_default DESC, id ASC");
|
|
$rows->execute([$uid]);
|
|
echo json_encode(['success'=>true,'methods'=>$rows->fetchAll()]);
|
|
break;
|
|
|
|
// ─── GAME ALIASES: get ────────────────────────────────
|
|
case 'game_aliases_get':
|
|
$uid = (int)($_GET['user_id'] ?? 0);
|
|
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
$stmt = db()->prepare("SELECT platform_slug, alias FROM game_aliases WHERE user_id=?");
|
|
$stmt->execute([$uid]);
|
|
$rows = $stmt->fetchAll();
|
|
$map = [];
|
|
foreach ($rows as $r) $map[$r['platform_slug']] = $r['alias'];
|
|
echo json_encode(['success'=>true,'aliases'=>$map]);
|
|
break;
|
|
|
|
// ─── GAME ALIASES: save all ───────────────────────────
|
|
case 'game_aliases_save_all':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
$aliases = $data['aliases'] ?? [];
|
|
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
$stmt = db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?)
|
|
ON DUPLICATE KEY UPDATE alias=VALUES(alias)");
|
|
$del = db()->prepare("DELETE FROM game_aliases WHERE user_id=? AND platform_slug=?");
|
|
foreach ($aliases as $slug => $alias) {
|
|
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($slug)));
|
|
$alias = substr(trim($alias), 0, 100);
|
|
if (!$slug) continue;
|
|
if ($alias === '') $del->execute([$uid, $slug]);
|
|
else $stmt->execute([$uid, $slug, $alias]);
|
|
}
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── PLATFORMS: admin list ────────────────────────────
|
|
case 'platforms_admin':
|
|
$rows = db()->query("SELECT * FROM platforms ORDER BY sort_order ASC, id ASC")->fetchAll();
|
|
echo json_encode(['success'=>true,'platforms'=>$rows]);
|
|
break;
|
|
|
|
// ─── PLATFORMS: create ────────────────────────────────
|
|
case 'platforms_create':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($d['slug'] ?? '')));
|
|
$name = substr(trim($d['name'] ?? ''), 0, 100);
|
|
$purl = substr(trim($d['player_url'] ?? ''), 0, 500);
|
|
$curl = substr(trim($d['console_url'] ?? ''), 0, 500);
|
|
$color= preg_match('/^#[0-9a-fA-F]{3,8}$/', $d['color']??'') ? $d['color'] : '#f0c040';
|
|
$sort = (int)($d['sort_order'] ?? 99);
|
|
$active=(int)(bool)($d['is_active'] ?? 1);
|
|
if (!$slug||!$name||!$purl) { echo json_encode(['success'=>false,'error'=>'Slug, name, and player URL required']); exit; }
|
|
try {
|
|
db()->prepare("INSERT INTO platforms (slug,name,player_url,console_url,color,sort_order,is_active) VALUES (?,?,?,?,?,?,?)")
|
|
->execute([$slug,$name,$purl,$curl,$color,$sort,$active]);
|
|
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
|
} catch (Exception $e) { echo json_encode(['success'=>false,'error'=>'Slug already exists']); }
|
|
break;
|
|
|
|
// ─── PLATFORMS: update ────────────────────────────────
|
|
case 'platforms_update':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($d['id'] ?? 0);
|
|
$name = substr(trim($d['name'] ?? ''), 0, 100);
|
|
$purl = substr(trim($d['player_url'] ?? ''), 0, 500);
|
|
$curl = substr(trim($d['console_url'] ?? ''), 0, 500);
|
|
$color= preg_match('/^#[0-9a-fA-F]{3,8}$/', $d['color']??'') ? $d['color'] : '#f0c040';
|
|
$sort = (int)($d['sort_order'] ?? 99);
|
|
$active=(int)(bool)($d['is_active'] ?? 1);
|
|
if (!$id||!$name||!$purl) { echo json_encode(['success'=>false,'error'=>'ID, name, and URL required']); exit; }
|
|
db()->prepare("UPDATE platforms SET name=?,player_url=?,console_url=?,color=?,sort_order=?,is_active=? WHERE id=?")
|
|
->execute([$name,$purl,$curl,$color,$sort,$active,$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── PLATFORMS: delete ────────────────────────────────
|
|
case 'platforms_delete':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$d = json_decode(file_get_contents('php://input'), true);
|
|
$id = (int)($d['id'] ?? 0);
|
|
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
|
db()->prepare("DELETE FROM platforms WHERE id=?")->execute([$id]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
case 'billing_get':
|
|
$uid = (int)($_GET['user_id'] ?? 0);
|
|
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
$stmt = db()->prepare("SELECT * FROM saved_billing WHERE user_id=?");
|
|
$stmt->execute([$uid]);
|
|
$row = $stmt->fetch();
|
|
// Admin sees masked card info
|
|
if ($row) {
|
|
$row['card_display'] = $row['card_brand'] && $row['card_last4']
|
|
? $row['card_brand'] . ' ····' . $row['card_last4'] : null;
|
|
// Don't expose raw sq_card_id
|
|
unset($row['sq_card_id']);
|
|
}
|
|
echo json_encode(['success'=>true,'billing'=>$row ?: null]);
|
|
break;
|
|
|
|
// ─── BILLING: save/update ────────────────────────────────
|
|
case 'billing_save':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
$stmt = db()->prepare("
|
|
INSERT INTO saved_billing (user_id,first_name,last_name,email,address,city,state,zip)
|
|
VALUES (?,?,?,?,?,?,?,?)
|
|
ON DUPLICATE KEY UPDATE
|
|
first_name=VALUES(first_name), last_name=VALUES(last_name),
|
|
email=VALUES(email), address=VALUES(address),
|
|
city=VALUES(city), state=VALUES(state), zip=VALUES(zip)
|
|
");
|
|
$stmt->execute([
|
|
$uid,
|
|
substr(trim($data['first_name']??''),0,80),
|
|
substr(trim($data['last_name'] ??''),0,80),
|
|
substr(strtolower(trim($data['email']??'')),0,150),
|
|
substr(trim($data['address'] ??''),0,200),
|
|
substr(trim($data['city'] ??''),0,80),
|
|
strtoupper(substr(trim($data['state']??''),0,2)),
|
|
substr(trim($data['zip'] ??''),0,10),
|
|
]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── BILLING: clear card ─────────────────────────────────
|
|
case 'billing_clear_card':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
db()->prepare("UPDATE saved_billing SET card_brand=NULL,card_last4=NULL,card_exp_month=NULL,card_exp_year=NULL,sq_card_id=NULL WHERE user_id=?")->execute([$uid]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── BILLING: clear all ──────────────────────────────────
|
|
case 'billing_clear_all':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
db()->prepare("DELETE FROM saved_billing WHERE user_id=?")->execute([$uid]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── RESEND VERIFICATION (from admin) ─────────────────────
|
|
case 'resend_verification':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$uid = (int)($data['user_id'] ?? 0);
|
|
$stmt = db()->prepare("SELECT email,alias FROM users WHERE id=?");
|
|
$stmt->execute([$uid]);
|
|
$user = $stmt->fetch();
|
|
if (!$user) { echo json_encode(['success'=>false,'error'=>'User not found']); exit; }
|
|
$result = resendVerification($user['email']);
|
|
echo json_encode($result);
|
|
break;
|
|
|
|
// ─── CHAT: inbox list ──────────────────────────────────
|
|
case 'chat_inbox':
|
|
$rows = db()->query("
|
|
SELECT u.id AS user_id, u.username, u.alias,
|
|
cm.message AS last_message, cm.sender AS last_sender,
|
|
cm.created_at AS last_time,
|
|
(SELECT COUNT(*) FROM chat_messages
|
|
WHERE user_id=u.id AND sender='user' AND is_read=0) AS unread_count
|
|
FROM users u
|
|
INNER JOIN chat_messages cm ON cm.id=(
|
|
SELECT id FROM chat_messages WHERE user_id=u.id ORDER BY id DESC LIMIT 1
|
|
)
|
|
ORDER BY cm.created_at DESC
|
|
")->fetchAll();
|
|
echo json_encode(['success'=>true,'inbox'=>$rows]);
|
|
break;
|
|
|
|
// ─── CHAT: full thread for one user ───────────────────
|
|
case 'chat_thread':
|
|
$tid = (int)($_GET['user_id'] ?? 0);
|
|
$since = (int)($_GET['since'] ?? 0);
|
|
if (!$tid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
$stmt = db()->prepare("SELECT id,sender,message,is_read,created_at FROM chat_messages WHERE user_id=? AND id>? ORDER BY id ASC LIMIT 300");
|
|
$stmt->execute([$tid, $since]);
|
|
// Mark user messages read
|
|
db()->prepare("UPDATE chat_messages SET is_read=1 WHERE user_id=? AND sender='user' AND is_read=0")->execute([$tid]);
|
|
$uStmt = db()->prepare("SELECT id,username,alias,tokens FROM users WHERE id=?");
|
|
$uStmt->execute([$tid]);
|
|
echo json_encode(['success'=>true,'messages'=>$stmt->fetchAll(),'user'=>$uStmt->fetch()]);
|
|
break;
|
|
|
|
// ─── CHAT: admin reply ────────────────────────────────
|
|
case 'chat_admin_send':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$tid = (int)($data['user_id'] ?? 0);
|
|
$msg = trim($data['message'] ?? '');
|
|
if (!$tid || empty($msg)) { echo json_encode(['success'=>false,'error'=>'Invalid']); exit; }
|
|
$stmt = db()->prepare("INSERT INTO chat_messages (user_id,sender,message) VALUES (?,'admin',?)");
|
|
$stmt->execute([$tid, $msg]);
|
|
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
|
break;
|
|
|
|
// ─── CHAT: clear single user thread ───────────────────
|
|
case 'chat_clear_thread':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$tid = (int)($data['user_id'] ?? 0);
|
|
if (!$tid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
|
db()->prepare("DELETE FROM chat_messages WHERE user_id=?")->execute([$tid]);
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
|
|
// ─── CHAT: clear ALL chats ────────────────────────────
|
|
case 'chat_clear_all':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
|
db()->exec("DELETE FROM chat_messages");
|
|
echo json_encode(['success'=>true]);
|
|
break;
|
|
case 'chat_unread':
|
|
$count = db()->query("SELECT COUNT(*) FROM chat_messages WHERE sender='user' AND is_read=0")->fetchColumn();
|
|
echo json_encode(['success'=>true,'count'=>(int)$count]);
|
|
break;
|
|
|
|
default:
|
|
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
|
}
|