Загрузка данных
<?php
require_once __DIR__ . '/includes/init.php';
if (is_logged_in()) {
redirect(url('profile.php'));
}
const LOGIN_MAX_ATTEMPTS = 5;
const LOGIN_LOCK_MINUTES = 10;
const LOGIN_WINDOW_MINUTES = 10;
function login_get_ip(): string
{
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
function login_get_lock_info(PDO $pdo, string $email, string $ipAddress): ?array
{
$stmt = $pdo->prepare('
SELECT
locked_until,
TIMESTAMPDIFF(SECOND, NOW(), locked_until) AS seconds_left
FROM login_attempts
WHERE email = :email
AND ip_address = :ip_address
AND locked_until IS NOT NULL
AND locked_until > NOW()
ORDER BY locked_until DESC
LIMIT 1
');
$stmt->execute([
'email' => $email,
'ip_address' => $ipAddress
]);
$row = $stmt->fetch();
return $row ?: null;
}
function login_count_failed_attempts(PDO $pdo, string $email, string $ipAddress): int
{
$stmt = $pdo->prepare('
SELECT COUNT(*) AS total
FROM login_attempts
WHERE email = :email
AND ip_address = :ip_address
AND is_success = 0
AND locked_until IS NULL
AND attempted_at >= DATE_SUB(NOW(), INTERVAL ' . LOGIN_WINDOW_MINUTES . ' MINUTE)
');
$stmt->execute([
'email' => $email,
'ip_address' => $ipAddress
]);
$row = $stmt->fetch();
return (int)($row['total'] ?? 0);
}
function login_record_failed_attempt(PDO $pdo, string $email, string $ipAddress): string
{
$failedAttempts = login_count_failed_attempts($pdo, $email, $ipAddress) + 1;
if ($failedAttempts >= LOGIN_MAX_ATTEMPTS) {
$stmt = $pdo->prepare('
INSERT INTO login_attempts (
email,
ip_address,
is_success,
locked_until
) VALUES (
:email,
:ip_address,
0,
DATE_ADD(NOW(), INTERVAL ' . LOGIN_LOCK_MINUTES . ' MINUTE)
)
');
$stmt->execute([
'email' => $email,
'ip_address' => $ipAddress
]);
return 'Слишком много неправильных попыток. Вход заблокирован на ' . LOGIN_LOCK_MINUTES . ' минут.';
}
$stmt = $pdo->prepare('
INSERT INTO login_attempts (
email,
ip_address,
is_success
) VALUES (
:email,
:ip_address,
0
)
');
$stmt->execute([
'email' => $email,
'ip_address' => $ipAddress
]);
$leftAttempts = LOGIN_MAX_ATTEMPTS - $failedAttempts;
return 'Неверный email или пароль. Осталось попыток: ' . $leftAttempts . '.';
}
function login_record_success(PDO $pdo, string $email, string $ipAddress): void
{
$stmt = $pdo->prepare('
DELETE FROM login_attempts
WHERE email = :email
AND ip_address = :ip_address
AND is_success = 0
');
$stmt->execute([
'email' => $email,
'ip_address' => $ipAddress
]);
$stmt = $pdo->prepare('
INSERT INTO login_attempts (
email,
ip_address,
is_success
) VALUES (
:email,
:ip_address,
1
)
');
$stmt->execute([
'email' => $email,
'ip_address' => $ipAddress
]);
}
if (is_post()) {
csrf_validate();
$email = strtolower(trim($_POST['email'] ?? ''));
$password = $_POST['password'] ?? '';
$ipAddress = login_get_ip();
if ($email === '' || $password === '') {
flash_set('danger', 'Введите email и пароль.');
redirect(url('login.php'));
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
flash_set('danger', 'Введите корректный email.');
redirect(url('login.php'));
}
$lock = login_get_lock_info($pdo, $email, $ipAddress);
if ($lock) {
$secondsLeft = max(1, (int)$lock['seconds_left']);
$minutesLeft = (int)ceil($secondsLeft / 60);
flash_set('danger', 'Вход временно заблокирован. Повторите попытку через ' . $minutesLeft . ' мин.');
redirect(url('login.php'));
}
$stmt = $pdo->prepare('
SELECT
u.id,
u.full_name,
u.email,
u.password_hash,
u.status,
r.code AS role_code
FROM users u
JOIN roles r ON r.id = u.role_id
WHERE LOWER(u.email) = :email
LIMIT 1
');
$stmt->execute([
'email' => $email
]);
$user = $stmt->fetch();
if (!$user || !password_verify($password, $user['password_hash'])) {
$message = login_record_failed_attempt($pdo, $email, $ipAddress);
flash_set('danger', $message);
redirect(url('login.php'));
}
if ($user['status'] !== 'active') {
flash_set('danger', 'Учётная запись недоступна. Обратитесь к администратору.');
redirect(url('login.php'));
}
login_record_success($pdo, $email, $ipAddress);
session_regenerate_id(true);
$_SESSION['user_id'] = (int)$user['id'];
$_SESSION['user'] = [
'id' => (int)$user['id'],
'full_name' => $user['full_name'],
'email' => $user['email'],
'role' => $user['role_code']
];
$_SESSION['role'] = $user['role_code'];
$_SESSION['user_role'] = $user['role_code'];
$_SESSION['user_email'] = $user['email'];
$_SESSION['user_name'] = $user['full_name'];
$updateStmt = $pdo->prepare('
UPDATE users
SET last_login_at = NOW()
WHERE id = :id
');
$updateStmt->execute([
'id' => $user['id']
]);
flash_set('success', 'Вы успешно вошли в систему.');
if ($user['role_code'] === 'admin') {
redirect(url('admin/index.php'));
}
if ($user['role_code'] === 'manager') {
redirect(url('manager/index.php'));
}
redirect(url('profile.php'));
}
require_once __DIR__ . '/includes/header.php';
?>
<div class="auth-page">
<form method="post" class="auth-card">
<?= csrf_field() ?>
<h1>Вход</h1>
<p class="text-muted">
Войдите в аккаунт для оформления заявок и работы с личным кабинетом.
</p>
<div class="mb-3">
<label class="form-label">Email</label>
<input
type="email"
name="email"
class="form-control"
required
autocomplete="email"
>
</div>
<div class="mb-3">
<label class="form-label">Пароль</label>
<input
type="password"
name="password"
class="form-control"
required
autocomplete="current-password"
>
</div>
<button type="submit" class="btn btn-primary w-100">
Войти
</button>
<div class="auth-links">
<a href="<?= e(url('register.php')) ?>">Создать аккаунт</a>
</div>
</form>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>