= лимита. * Вызывается ДО записи текущей попытки и до проверки пароля. */ function rate_check( PDO $pdo, string $ip, string $action, int $max = GOLEARN_RATE_MAX ): void { $stmt = $pdo->prepare( 'SELECT COUNT(*) AS n FROM rate_log WHERE ip = ? AND action = ? AND ts > ?' ); $stmt->execute([$ip, $action, time() - GOLEARN_RATE_WINDOW]); $row = $stmt->fetch(); if (is_array($row) && (int)$row['n'] >= $max) { error_out(429, 'rate_limited'); } } /** Записать попытку (после прохождения rate_check). */ function rate_record(PDO $pdo, string $ip, string $action): void { $stmt = $pdo->prepare( 'INSERT INTO rate_log (ip, action, ts) VALUES (?, ?, ?)' ); $stmt->execute([$ip, $action, time()]); } /** Сбросить счётчик ip+action (успешный login). */ function rate_clear(PDO $pdo, string $ip, string $action): void { $stmt = $pdo->prepare('DELETE FROM rate_log WHERE ip = ? AND action = ?'); $stmt->execute([$ip, $action]); }