togethere.cloud/public_html/api/suspendUser.php

288 lines
16 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/admin_bootstrap.php';
require_once __DIR__ . '/../includes/smtp_helper.php';
$admin = admin_require_auth();
$adminId = $admin['user_id'];
$adminUsername = $admin['username'];
$supportEmail = 'wspolpraca@togethere.cloud';
$supportUrl = 'https://togethere.cloud/bok/';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
admin_json_error('Metoda niedozwolona', 405);
}
$pdo = admin_get_pdo();
// Ensure required columns exist.
$columnsToAdd = [
'suspension_reason' => "ALTER TABLE users ADD COLUMN suspension_reason VARCHAR(500) NULL AFTER account_suspended",
'suspended_until' => "ALTER TABLE users ADD COLUMN suspended_until DATETIME NULL AFTER suspension_reason",
'suspended_by' => "ALTER TABLE users ADD COLUMN suspended_by INT UNSIGNED NULL AFTER suspended_until",
];
$db = (string)$pdo->query('SELECT DATABASE()')->fetchColumn();
foreach ($columnsToAdd as $col => $sql) {
$check = $pdo->prepare('SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = ? AND column_name = ?');
$check->execute([$db, 'users', $col]);
if ((int)$check->fetchColumn() === 0) {
try {
$pdo->exec($sql);
} catch (Throwable $e) {
}
}
}
// Ensure history table exists.
try {
$pdo->exec("CREATE TABLE IF NOT EXISTS user_account_history (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED NOT NULL,
action ENUM('suspend', 'unsuspend') NOT NULL,
reason TEXT NULL,
suspended_until DATETIME NULL,
performed_by INT UNSIGNED NULL,
performed_by_username VARCHAR(50) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_uah_user_id (user_id),
KEY idx_uah_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
} catch (Throwable $e) {
}
$body = admin_read_json_body();
$action = isset($body['action']) ? (string)$body['action'] : '';
$userId = isset($body['user_id']) ? (int)$body['user_id'] : 0;
if ($userId <= 0) {
admin_json_error('Nieprawidłowy user_id');
}
if (!in_array($action, ['suspend', 'unsuspend'], true)) {
admin_json_error('Nieprawidłowa akcja');
}
// Fetch target user
$stmtUser = $pdo->prepare("SELECT id, username, email, role, account_suspended FROM users WHERE id = ? LIMIT 1");
$stmtUser->execute([$userId]);
$targetUser = $stmtUser->fetch(PDO::FETCH_ASSOC);
if (!$targetUser) {
admin_json_error('Użytkownik nie istnieje', 404);
}
$targetRole = strtolower((string)($targetUser['role'] ?? 'user'));
if ((int)$targetUser['id'] === (int)$adminId) {
admin_json_error('Nie możesz zarządzać swoim kontem w tym widoku.', 403);
}
if ($targetRole === 'admin') {
admin_json_error('Nie można zarządzać kontami administratorów w tym widoku.', 403);
}
$targetEmail = trim((string)($targetUser['email'] ?? ''));
if ($action === 'suspend') {
$reason = trim((string)($body['reason'] ?? ''));
if ($reason === '') {
admin_json_error('Powód zawieszenia jest wymagany');
}
$suspendedUntilRaw = (string)($body['suspended_until'] ?? 'permanent');
$suspendedUntil = null;
$suspendedUntilDisplay = 'bezterminowo';
if ($suspendedUntilRaw !== 'permanent') {
$ts = strtotime($suspendedUntilRaw);
if ($ts === false || $ts <= time()) {
admin_json_error('Nieprawidłowa data zawieszenia - musi być w przyszłości');
}
$suspendedUntil = date('Y-m-d H:i:s', $ts);
$suspendedUntilDisplay = date('d.m.Y H:i', $ts);
}
try {
$pdo->prepare("UPDATE users SET account_suspended = 1, suspension_reason = ?, suspended_until = ?, suspended_by = ?, wallet_status = 'suspended' WHERE id = ?")
->execute([$reason, $suspendedUntil, $adminId, $userId]);
} catch (Throwable $e) {
try {
$pdo->prepare("UPDATE users SET account_suspended = 1 WHERE id = ?")->execute([$userId]);
} catch (Throwable $e2) {
admin_json_error('Błąd aktualizacji bazy danych: ' . $e2->getMessage(), 500);
}
}
try {
$pdo->prepare("INSERT INTO user_account_history (user_id, action, reason, suspended_until, performed_by, performed_by_username) VALUES (?, 'suspend', ?, ?, ?, ?)")
->execute([$userId, $reason, $suspendedUntil, $adminId, $adminUsername]);
} catch (Throwable $e) {
}
$emailSent = false;
$emailError = '';
try {
$untilHtml = $suspendedUntil ? '<strong>' . htmlspecialchars($suspendedUntilDisplay, ENT_QUOTES, 'UTF-8') . '</strong>' : '<strong>bezterminowo</strong>';
$safeUsernameSuspend = htmlspecialchars((string)$targetUser['username'], ENT_QUOTES, 'UTF-8');
$safeReasonSuspend = nl2br(htmlspecialchars($reason, ENT_QUOTES, 'UTF-8'));
$safeSupportEmailSuspend = htmlspecialchars($supportEmail, ENT_QUOTES, 'UTF-8');
$safeSupportUrlSuspend = htmlspecialchars($supportUrl, ENT_QUOTES, 'UTF-8');
$emailBody = '<!DOCTYPE html><html lang="pl"><head><meta charset="UTF-8"><title>Konto zawieszone</title></head><body style="margin:0;padding:0;background:#f4f4f4;font-family:Arial,sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f4f4f4;padding:30px 0;"><tr><td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.1);">
<tr><td style="background:linear-gradient(135deg,#dc3545,#c82333);padding:30px 40px;text-align:center;">
<h1 style="color:#fff;margin:0;font-size:26px;letter-spacing:1px;">&#9888; Konto zawieszone</h1>
<p style="color:#ffc9ce;margin:8px 0 0;font-size:14px;">Powiadomienie dotycz&#261;ce Twojego konta TOGETHERE</p>
</td></tr>
<tr><td style="padding:35px 40px;">
<p style="font-size:16px;color:#333;margin-top:0;">Cze&#347;&#263; <strong>' . $safeUsernameSuspend . '</strong>,</p>
<p style="font-size:15px;color:#555;">Informujemy, &#380;e Twoje konto w serwisie <strong>TOGETHERE GAMES</strong> zosta&#322;o <strong>zawieszone</strong> przez administracj&#281; serwisu.</p>
<table width="100%" cellpadding="14" cellspacing="0" style="background:#fff5f5;border:1px solid #f5c6cb;border-radius:6px;margin:24px 0;">
<tr><td style="padding-bottom:10px;"><span style="font-size:13px;text-transform:uppercase;color:#999;letter-spacing:0.5px;">Pow&oacute;d zawieszenia</span><br><span style="font-size:15px;color:#333;margin-top:4px;display:block;">' . $safeReasonSuspend . '</span></td></tr>
<tr><td style="border-top:1px solid #f5c6cb;padding-top:10px;"><span style="font-size:13px;text-transform:uppercase;color:#999;letter-spacing:0.5px;">Zawieszone do</span><br><span style="font-size:15px;color:#333;margin-top:4px;display:block;">' . $untilHtml . '</span></td></tr>
</table>
<p style="font-size:15px;color:#333;font-weight:bold;">Co to oznacza?</p>
<ul style="font-size:14px;color:#555;line-height:1.8;padding-left:20px;">
<li>Nie mo&#380;esz si&#281; zalogowa&#263; na swoje konto.</li>
<li>Dost&#281;p do funkcji serwisu jest zablokowany do czasu odwieszenia.</li>
<li>Twoje dane i historia s&#261; bezpiecznie przechowywane.</li>
' . ($suspendedUntil ? '<li>Po up&#322;yni&#281;ciu terminu (<strong>' . htmlspecialchars($suspendedUntilDisplay, ENT_QUOTES, 'UTF-8') . '</strong>) konto zostanie automatycznie odwieszone.</li>' : '') . '
</ul>
<p style="font-size:15px;color:#333;font-weight:bold;">Chcesz z&#322;o&#380;y&#263; odwo&#322;anie?</p>
<p style="font-size:14px;color:#555;">Je&#380;eli uwa&#380;asz, &#380;e decyzja zosta&#322;a podj&#281;ta b&#322;&#281;dnie, mo&#380;esz skontaktowa&#263; si&#281; z naszym Biurem Obs&#322;ugi Klienta:</p>
<table width="100%" cellpadding="0" cellspacing="0" style="margin:16px 0;">
<tr>
<td style="padding:6px 0;">&#128231; E-mail: <a href="mailto:' . $safeSupportEmailSuspend . '" style="color:#dc3545;text-decoration:none;font-weight:bold;">' . $safeSupportEmailSuspend . '</a></td>
</tr>
<tr>
<td style="padding:6px 0;">&#127760; BOK online: <a href="' . $safeSupportUrlSuspend . '" style="color:#dc3545;text-decoration:none;font-weight:bold;">' . $safeSupportUrlSuspend . '</a></td>
</tr>
</table>
<p style="font-size:13px;color:#888;margin-top:8px;">W wiadomo&#347;ci podaj sw&oacute;j login oraz opisz sytuacj&#281; &#8212; postaramy si&#281; odpowiedzie&#263; mo&#380;liwie szybko.</p>
<hr style="border:none;border-top:1px solid #eee;margin:28px 0;">
<p style="font-size:13px;color:#aaa;text-align:center;margin:0;">Wiadomo&#347;&#263; wygenerowana automatycznie &mdash; prosimy nie odpowiada&#263; bezpo&#347;rednio na ten e-mail.<br>TOGETHERE GAMES &bull; <a href="https://togethere.cloud" style="color:#aaa;">togethere.cloud</a></p>
</td></tr></table>
</td></tr></table>
</body></html>';
$emailSent = (bool)sendEmailSMTP($targetEmail, 'Twoje konto zostało zawieszone - Wspólnie', $emailBody);
if (!$emailSent) {
// Fallback przez mail() jeśli SMTP nie zadziałał
$mHeaders = "MIME-Version: 1.0\r\n";
$mHeaders .= "Content-Type: text/html; charset=UTF-8\r\n";
$mHeaders .= "From: TOGETHERE GAMES <noreply@togethere.cloud>\r\n";
$mHeaders .= "Reply-To: " . $supportEmail . "\r\n";
$emailSent = @mail($targetEmail, '=?UTF-8?B?' . base64_encode('Twoje konto zostało zawieszone - Wspólnie') . '?=', $emailBody, $mHeaders);
if (!$emailSent) {
$emailError = 'Wysyłka nie powiodła się (SMTP + mail). Sprawdź smtp_debug.log.';
}
}
} catch (Throwable $e) {
$emailError = $e->getMessage();
}
$msg = 'Konto użytkownika zostało zawieszone.';
if (!$emailSent) {
$msg .= ' ⚠️ Email NIE został wysłany: ' . $emailError;
}
admin_json_response(['success' => true, 'message' => $msg, 'email_sent' => $emailSent]);
}
// unsuspend
$unsuspendReason = trim((string)($body['reason'] ?? ''));
if ($unsuspendReason === '') {
$unsuspendReason = 'Decyzja administracyjna po weryfikacji sytuacji.';
}
try {
$pdo->prepare("UPDATE users SET account_suspended = 0, suspension_reason = NULL, suspended_until = NULL, suspended_by = NULL, wallet_status = 'active' WHERE id = ?")
->execute([$userId]);
} catch (Throwable $e) {
try {
$pdo->prepare("UPDATE users SET account_suspended = 0 WHERE id = ?")->execute([$userId]);
} catch (Throwable $e2) {
admin_json_error('Błąd aktualizacji bazy danych: ' . $e2->getMessage(), 500);
}
}
try {
$pdo->prepare("INSERT INTO user_account_history (user_id, action, reason, suspended_until, performed_by, performed_by_username) VALUES (?, 'unsuspend', ?, NULL, ?, ?)")
->execute([$userId, $unsuspendReason, $adminId, $adminUsername]);
} catch (Throwable $e) {
}
$emailSent = false;
$emailError = '';
try {
$safeUsername = htmlspecialchars((string)$targetUser['username'], ENT_QUOTES, 'UTF-8');
$safeReason = nl2br(htmlspecialchars($unsuspendReason, ENT_QUOTES, 'UTF-8'));
$safeSupportEmail = htmlspecialchars($supportEmail, ENT_QUOTES, 'UTF-8');
$safeSupportUrl = htmlspecialchars($supportUrl, ENT_QUOTES, 'UTF-8');
$emailBody = '<!DOCTYPE html><html lang="pl"><head><meta charset="UTF-8"><title>Konto aktywne</title></head><body style="margin:0;padding:0;background:#f4f4f4;font-family:Arial,sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f4f4f4;padding:30px 0;"><tr><td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.1);">
<tr><td style="background:linear-gradient(135deg,#28a745,#1e7e34);padding:30px 40px;text-align:center;">
<h1 style="color:#fff;margin:0;font-size:26px;letter-spacing:1px;">&#10003; Konto odwieszone</h1>
<p style="color:#c3f0cc;margin:8px 0 0;font-size:14px;">Twoje konto TOGETHERE jest ponownie aktywne</p>
</td></tr>
<tr><td style="padding:35px 40px;">
<p style="font-size:16px;color:#333;margin-top:0;">Cze&#347;&#263; <strong>' . $safeUsername . '</strong>,</p>
<p style="font-size:15px;color:#555;">Mamy dla Ciebie dobr&#261; wiadomo&#347;&#263; &#8212; Twoje konto w serwisie <strong>TOGETHERE GAMES</strong> zosta&#322;o <strong>odwieszone</strong> i jest w pe&#322;ni aktywne.</p>
<table width="100%" cellpadding="14" cellspacing="0" style="background:#f0fff4;border:1px solid #b2dfdb;border-radius:6px;margin:24px 0;">
<tr><td><span style="font-size:13px;text-transform:uppercase;color:#999;letter-spacing:0.5px;">Pow&oacute;d odwieszenia</span><br><span style="font-size:15px;color:#333;margin-top:4px;display:block;">' . $safeReason . '</span></td></tr>
<tr><td style="border-top:1px solid #b2dfdb;"><span style="font-size:13px;text-transform:uppercase;color:#999;letter-spacing:0.5px;">Status konta</span><br><span style="font-size:15px;font-weight:bold;color:#28a745;margin-top:4px;display:block;">&#9679; Aktywne &#8212; dost&#281;p w pe&#322;ni przywr&oacute;cony</span></td></tr>
</table>
<p style="font-size:15px;color:#333;font-weight:bold;">Co zosta&#322;o przywr&oacute;cone?</p>
<ul style="font-size:14px;color:#555;line-height:1.8;padding-left:20px;">
<li>Mo&#380;liwo&#347;&#263; logowania na konto.</li>
<li>Pe&#322;ny dost&#281;p do funkcji serwisu (turnieje, mecze, profil).</li>
<li>Historia i dane konta zosta&#322;y zachowane bez zmian.</li>
</ul>
<p style="font-size:15px;color:#333;font-weight:bold;">Pami&#281;taj o zasadach</p>
<p style="font-size:14px;color:#555;">Prosimy o zapoznanie si&#281; z regulaminem serwisu i przestrzeganie zasad spo&#322;eczno&#347;ci TOGETHERE GAMES. Kolejne naruszenie mo&#380;e skutkowa&#263; sta&#322;ym zablokowaniem konta.</p>
<p style="font-size:14px;color:#555;">Je&#380;eli masz pytania lub potrzebujesz pomocy:</p>
<table width="100%" cellpadding="0" cellspacing="0" style="margin:12px 0 20px;">
<tr>
<td style="padding:6px 0;">&#128231; E-mail: <a href="mailto:' . $safeSupportEmail . '" style="color:#28a745;text-decoration:none;font-weight:bold;">' . $safeSupportEmail . '</a></td>
</tr>
<tr>
<td style="padding:6px 0;">&#127760; BOK online: <a href="' . $safeSupportUrl . '" style="color:#28a745;text-decoration:none;font-weight:bold;">' . $safeSupportUrl . '</a></td>
</tr>
</table>
<p style="font-size:15px;color:#333;">Witamy ponownie w serwisie i &#380;yczymy udanej gry! &#127942;</p>
<hr style="border:none;border-top:1px solid #eee;margin:28px 0;">
<p style="font-size:13px;color:#aaa;text-align:center;margin:0;">Wiadomo&#347;&#263; wygenerowana automatycznie &mdash; prosimy nie odpowiada&#263; bezpo&#347;rednio na ten e-mail.<br>TOGETHERE GAMES &bull; <a href="https://togethere.cloud" style="color:#aaa;">togethere.cloud</a></p>
</td></tr></table>
</td></tr></table>
</body></html>';
$emailSent = (bool)sendEmailSMTP($targetEmail, 'Status konta - Wspolnie', $emailBody);
if (!$emailSent) {
// Awaryjnie wyślij przez mail() niezależnie od helpera SMTP.
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
$headers .= "From: TOGETHERE GAMES <noreply@togethere.cloud>\r\n";
$headers .= "Reply-To: " . $supportEmail . "\r\n";
$emailSent = @mail($targetEmail, 'Status konta - Wspolnie', $emailBody, $headers);
if (!$emailSent) {
$emailError = 'Wysyłka nie powiodła się (SMTP + mail). Sprawdź smtp_debug.log i konfigurację serwera poczty.';
}
}
} catch (Throwable $e) {
$emailError = $e->getMessage();
}
$msg = 'Konto użytkownika zostało odwieszone.';
if (!$emailSent) {
$msg .= ' ⚠️ Email NIE został wysłany: ' . $emailError;
}
admin_json_response(['success' => true, 'message' => $msg, 'email_sent' => $emailSent]);