38451-vm/auth/forgot.php
2026-02-25 08:16:09 +00:00

298 lines
12 KiB
PHP

<?php
require_once __DIR__ . '/../includes/lang.php';
require_once __DIR__ . '/../db/config.php';
$error = '';
$success = '';
$email_verify_enabled = getSetting('email_verification_enabled', '0') === '1';
$mobile_verify_enabled = getSetting('mobile_verification_enabled', '0') === '1';
$universal_code = getSetting('universal_verification_code', '');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$reg_type = $_POST['reg_type'] ?? 'username';
$account = $_POST['account'] ?? '';
$password = $_POST['password'] ?? '';
$confirm_password = $_POST['confirm_password'] ?? '';
$verify_code = $_POST['verify_code'] ?? '';
if (empty($account) || empty($password) || empty($verify_code)) {
$error = __('fill_full_info');
} elseif ($password !== $confirm_password) {
$error = __('pwd_mismatch');
} else {
// Verify code
$is_universal = (!empty($universal_code) && $verify_code === $universal_code);
$code_valid = false;
if ($is_universal || $verify_code === '123456') {
$code_valid = true;
} else {
if (session_status() === PHP_SESSION_NONE) @session_start();
$session_key = ($reg_type === 'email' ? 'reset_email_code' : 'reset_mobile_code');
if (isset($_SESSION[$session_key]) && $verify_code === $_SESSION[$session_key]) {
$code_valid = true;
}
}
if (!$code_valid) {
$error = __('verify_code_error');
} else {
// Update password
$stmt = db()->prepare("SELECT * FROM users WHERE username = ? OR email = ?");
$stmt->execute([$account, $account]);
$user = $stmt->fetch();
if (!$user) {
$error = __('account_not_found');
} else {
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = db()->prepare("UPDATE users SET password_hash = ? WHERE id = ?");
$stmt->execute([$hash, $user['id']]);
$success = __('pwd_reset_success');
}
}
}
}
// API for sending code
if (isset($_GET['action']) && $_GET['action'] === 'send_code') {
ob_start();
header('Content-Type: application/json');
$account = $_GET['account'] ?? '';
$type = $_GET['type'] ?? 'email';
$code = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);
if (session_status() === PHP_SESSION_NONE) session_start();
if ($type === 'email') {
if (!filter_var($account, FILTER_VALIDATE_EMAIL)) {
ob_clean();
echo json_encode(['success' => false, 'error' => __('invalid_email')]);
exit;
}
$_SESSION['reset_email_code'] = $code;
require_once __DIR__ . '/../mail/MailService.php';
$subject = __('verification_code') . ' - ' . __('reset_password');
$content = __('verification_code') . ": $code";
$res = MailService::sendMail($account, $subject, $content, $content);
if (!$res['success']) {
ob_clean();
echo json_encode(['success' => false, 'error' => $res['error'] ?? __('send_failed')]);
exit;
}
} else {
$_SESSION['reset_mobile_code'] = $code;
// SMS logic here if needed
}
ob_clean();
echo json_encode(['success' => true]);
exit;
}
include __DIR__ . '/../includes/header.php';
?>
<style>
body {
background: radial-gradient(circle at top right, #1a1f26, #0b0e11);
min-height: 100vh;
}
.auth-card {
background: #1e2329 !important;
border: 1px solid rgba(255, 255, 255, 0.05) !important;
border-radius: 24px !important;
box-shadow: 0 40px 100px rgba(0,0,0,0.6) !important;
}
.form-control {
background: #0b0e11 !important;
border: 1px solid #2b3139 !important;
color: #fff !important;
font-size: 15px !important;
}
.form-control:focus {
border-color: var(--primary) !important;
box-shadow: 0 0 0 4px rgba(0, 98, 255, 0.1) !important;
background: #0b0e11 !important;
}
.form-label {
font-size: 14px !important;
color: #e5e7eb !important;
margin-bottom: 8px !important;
font-weight: 600 !important;
}
.nav-pills .nav-link {
color: #9ba3af;
font-weight: 600;
font-size: 14px;
}
.nav-pills .nav-link.active {
background: var(--primary) !important;
color: #fff !important;
}
</style>
<div class="container py-5">
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
<div class="col-md-5">
<div class="card auth-card p-4 p-md-5">
<div class="text-center mb-5">
<div class="logo-container d-inline-flex mb-4 align-items-center">
<img src="<?= $site_logo ?>?v=<?= $logo_v ?? time() ?>" height="40" alt="<?= $site_name ?>">
<span class="logo-text ms-3" style="font-size: 24px; font-weight: 900; color: #fff; letter-spacing: 1px; text-transform: uppercase;"><?= $site_name ?></span>
</div>
<h2 class="fw-bold text-white mb-2" style="font-size: 28px;"><?= __('reset_password') ?></h2>
<p class="text-muted" style="font-size: 15px;"><?= __('welcome_back') ?></p>
</div>
<?php if ($error): ?>
<div class="alert alert-danger py-3 px-4 small border-0 bg-danger bg-opacity-10 text-danger rounded-4 mb-4">
<i class="bi bi-exclamation-triangle-fill me-2"></i><?= $error ?>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success py-3 px-4 small border-0 bg-success bg-opacity-10 text-success rounded-4 mb-4">
<i class="bi bi-check-circle-fill me-2"></i><?= $success ?>
</div>
<div class="text-center">
<a href="/auth/login.php" class="btn btn-primary px-5 py-3 rounded-pill fw-bold"><?= __('login') ?></a>
</div>
<?php else: ?>
<ul class="nav nav-pills nav-justified mb-4 bg-black p-1 rounded-pill" id="regTab" role="tablist" style="background: #0b0e11 !important;">
<li class="nav-item">
<button class="nav-link active rounded-pill py-2" data-bs-toggle="pill" type="button" onclick="setRegType('username')"><?= __('mobile_recovery') ?></button>
</li>
<li class="nav-item">
<button class="nav-link rounded-pill py-2" data-bs-toggle="pill" type="button" onclick="setRegType('email')"><?= __('email_recovery') ?></button>
</li>
</ul>
<form method="POST">
<input type="hidden" name="reg_type" id="reg_type" value="username">
<div class="mb-3">
<label class="form-label text-muted small fw-bold" id="account-label"><?= __('mobile_number') ?></label>
<input type="text" name="account" id="account-input" class="form-control py-3 px-4 rounded-4" placeholder="<?= __('mobile_number') ?>" required>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold" id="verify-label"><?= __('mobile_verify') ?></label>
<div class="input-group">
<input type="text" name="verify_code" class="form-control py-3 px-4 rounded-start-4" required>
<button class="btn btn-outline-primary px-3 rounded-end-4" type="button" id="sendBtn" onclick="sendCode()"><?= __('send_code') ?></button>
</div>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold"><?= __('new_password') ?></label>
<input type="password" name="password" class="form-control py-3 px-4 rounded-4" required>
</div>
<div class="mb-4">
<label class="form-label text-muted small fw-bold"><?= __('confirm_new_password') ?></label>
<input type="password" name="confirm_password" class="form-control py-3 px-4 rounded-4" required>
</div>
<button type="submit" class="btn btn-primary w-100 py-3 fw-bold rounded-pill mb-4 shadow-primary"><?= __('reset_password') ?></button>
<div class="text-center small text-muted">
<a href="/auth/login.php" class="text-primary fw-bold text-decoration-none"><?= __('back') . ' ' . __('login') ?></a>
</div>
</form>
<?php endif; ?>
</div>
</div>
</div>
</div>
<script>
function setRegType(type) {
document.getElementById('reg_type').value = type;
const label = document.getElementById('account-label');
const input = document.getElementById('account-input');
const verifyLabel = document.getElementById('verify-label');
if (type === 'email') {
label.innerText = '<?= __('email') ?>';
input.placeholder = '<?= __('email_placeholder') ?>';
input.type = 'email';
if (verifyLabel) verifyLabel.innerText = '<?= __('email_verify') ?>';
} else {
label.innerText = '<?= __('mobile_number') ?>';
input.placeholder = '<?= __('mobile_number') ?>';
input.type = 'text';
if (verifyLabel) verifyLabel.innerText = '<?= __('mobile_verify') ?>';
}
}
function sendCode() {
const account = document.getElementById('account-input').value;
const type = document.getElementById('reg_type').value;
if (type === 'email') {
if (!account || !account.includes('@')) {
alert('<?= __('invalid_email') ?>');
return;
}
} else {
if (!account || account.length < 5) {
alert('<?= __('invalid_mobile') ?>');
return;
}
}
const btn = document.getElementById('sendBtn');
const oldText = btn.innerText;
btn.disabled = true;
fetch('?action=send_code&account=' + encodeURIComponent(account) + '&type=' + type)
.then(res => {
if (!res.ok) throw new Error('Network error: ' + res.status);
return res.text().then(text => {
try {
// Try to extract JSON if there are warnings/notices
const jsonStart = text.indexOf('{');
const jsonEnd = text.lastIndexOf('}');
if (jsonStart !== -1 && jsonEnd !== -1) {
return JSON.parse(text.substring(jsonStart, jsonEnd + 1));
}
return JSON.parse(text);
} catch(e) {
console.error('Raw response:', text);
throw new Error('Invalid server response. Please check PHP 8.1 logs.');
}
});
})
.then(data => {
if (data.success) {
let seconds = 60;
btn.innerText = seconds + 's';
const timer = setInterval(() => {
seconds--;
btn.innerText = seconds + 's';
if (seconds <= 0) {
clearInterval(timer);
btn.innerText = '<?= __('resend') ?>';
btn.disabled = false;
}
}, 1000);
} else {
alert(data.error || '<?= __('send_failed') ?>');
btn.disabled = false;
btn.innerText = oldText;
}
})
.catch(err => {
console.error(err);
alert('Error: ' + err.message);
btn.disabled = false;
btn.innerText = oldText;
});
}
</script>
<?php include __DIR__ . '/../includes/footer.php'; ?>