300 lines
12 KiB
PHP
300 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();
|
|
error_reporting(0);
|
|
ini_set('display_errors', 0);
|
|
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'; ?>
|