Remove usage old model from Throttling

[MAILPOET-3032]
This commit is contained in:
Jan Lysý
2021-04-02 17:11:07 +02:00
committed by Veljko V
parent 4631fb7320
commit 4d56020071
3 changed files with 77 additions and 40 deletions

View File

@ -4,6 +4,7 @@ namespace MailPoet\Subscribers;
use MailPoet\Doctrine\Repository; use MailPoet\Doctrine\Repository;
use MailPoet\Entities\SubscriberIPEntity; use MailPoet\Entities\SubscriberIPEntity;
use MailPoetVendor\Carbon\Carbon;
/** /**
* @extends Repository<SubscriberIPEntity> * @extends Repository<SubscriberIPEntity>
@ -12,4 +13,39 @@ class SubscriberIPsRepository extends Repository {
protected function getEntityClassName() { protected function getEntityClassName() {
return SubscriberIPEntity::class; return SubscriberIPEntity::class;
} }
public function findOneByIPAndCreatedAtAfterTimeInSeconds(string $ip, int $seconds): ?SubscriberIPEntity {
return $this->entityManager->createQueryBuilder()
->select('sip')
->from(SubscriberIPEntity::class, 'sip')
->where('sip.ip = :ip')
->andWhere('sip.createdAt >= :timeThreshold')
->setParameter('ip', $ip)
->setParameter('timeThreshold', (new Carbon())->subSeconds($seconds))
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
public function getCountByIPAndCreatedAtAfterTimeInSeconds(string $ip, int $seconds): int {
return $this->entityManager->createQueryBuilder()
->select('COUNT(sip)')
->from(SubscriberIPEntity::class, 'sip')
->where('sip.ip = :ip')
->andWhere('sip.createdAt >= :timeThreshold')
->setParameter('ip', $ip)
->setParameter('timeThreshold', (new Carbon())->subSeconds($seconds))
->getQuery()
->getSingleScalarResult();
}
public function deleteCreatedAtBeforeTimeInSeconds(int $seconds): int {
return (int)$this->entityManager->createQueryBuilder()
->delete()
->from(SubscriberIPEntity::class, 'sip')
->where('sip.createdAt < :timeThreshold')
->setParameter('timeThreshold', (new Carbon())->subSeconds($seconds))
->getQuery()
->execute();
}
} }

View File

@ -2,42 +2,37 @@
namespace MailPoet\Subscription; namespace MailPoet\Subscription;
use MailPoet\Models\SubscriberIP; use MailPoet\Entities\SubscriberIPEntity;
use MailPoet\Subscribers\SubscriberIPsRepository;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
use MailPoet\WP\Functions as WPFunctions; use MailPoet\WP\Functions as WPFunctions;
class Throttling { class Throttling {
/** @var SubscriberIPsRepository */
private $subscriberIPsRepository;
/** @var WPFunctions */ /** @var WPFunctions */
private $wp; private $wp;
public function __construct(WPFunctions $wp) { public function __construct(SubscriberIPsRepository $subscriberIPsRepository, WPFunctions $wp) {
$this->wp = $wp; $this->wp = $wp;
$this->subscriberIPsRepository = $subscriberIPsRepository;
} }
public function throttle() { public function throttle() {
$subscriptionLimitEnabled = $this->wp->applyFilters('mailpoet_subscription_limit_enabled', true); $subscriptionLimitEnabled = $this->wp->applyFilters('mailpoet_subscription_limit_enabled', true);
$subscriptionLimitWindow = $this->wp->applyFilters('mailpoet_subscription_limit_window', DAY_IN_SECONDS); $subscriptionLimitWindow = (int)$this->wp->applyFilters('mailpoet_subscription_limit_window', DAY_IN_SECONDS);
$subscriptionLimitBase = $this->wp->applyFilters('mailpoet_subscription_limit_base', MINUTE_IN_SECONDS); $subscriptionLimitBase = (int)$this->wp->applyFilters('mailpoet_subscription_limit_base', MINUTE_IN_SECONDS);
$subscriberIp = Helpers::getIP(); $subscriberIp = Helpers::getIP();
if ($subscriptionLimitEnabled && !$this->wp->isUserLoggedIn()) { if ($subscriptionLimitEnabled && !$this->wp->isUserLoggedIn()) {
if (!empty($subscriberIp)) { if (!empty($subscriberIp)) {
$subscriptionCount = SubscriberIP::where('ip', $subscriberIp) $subscriptionCount = $this->subscriberIPsRepository->getCountByIPAndCreatedAtAfterTimeInSeconds($subscriberIp, $subscriptionLimitWindow);
->whereRaw(
'(`created_at` >= NOW() - INTERVAL ? SECOND)',
[(int)$subscriptionLimitWindow]
)->count();
if ($subscriptionCount > 0) { if ($subscriptionCount > 0) {
$timeout = $subscriptionLimitBase * pow(2, $subscriptionCount - 1); $timeout = $subscriptionLimitBase * pow(2, $subscriptionCount - 1);
$existingUser = SubscriberIP::where('ip', $subscriberIp) $existingUser = $this->subscriberIPsRepository->findOneByIPAndCreatedAtAfterTimeInSeconds($subscriberIp, $timeout);
->whereRaw(
'(`created_at` >= NOW() - INTERVAL ? SECOND)',
[(int)$timeout]
)->findOne();
if (!empty($existingUser)) { if (!empty($existingUser)) {
return $timeout; return $timeout;
} }
@ -45,21 +40,23 @@ class Throttling {
} }
} }
$ip = SubscriberIP::create(); if ($subscriberIp !== null) {
$ip->ip = $subscriberIp; $ip = new SubscriberIPEntity($subscriberIp);
$ip->save(); $existingIp = $this->subscriberIPsRepository->findOneBy(['ip' => $ip->getIP(), 'createdAt' => $ip->getCreatedAt()]);
if (!$existingIp) {
$this->subscriberIPsRepository->persist($ip);
$this->subscriberIPsRepository->flush();
}
}
$this->purge(); $this->purge();
return false; return false;
} }
public function purge() { public function purge(): void {
$interval = $this->wp->applyFilters('mailpoet_subscription_purge_window', MONTH_IN_SECONDS); $interval = $this->wp->applyFilters('mailpoet_subscription_purge_window', MONTH_IN_SECONDS);
return SubscriberIP::whereRaw( $this->subscriberIPsRepository->deleteCreatedAtBeforeTimeInSeconds($interval);
'(`created_at` < NOW() - INTERVAL ? SECOND)',
[$interval]
)->deleteMany();
} }
public function secondsToTimeString($seconds): string { public function secondsToTimeString($seconds): string {

View File

@ -2,7 +2,8 @@
namespace MailPoet\Subscription; namespace MailPoet\Subscription;
use MailPoet\Models\SubscriberIP; use MailPoet\Entities\SubscriberIPEntity;
use MailPoet\Subscribers\SubscriberIPsRepository;
use MailPoet\WP\Functions as WPFunctions; use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Carbon\Carbon; use MailPoetVendor\Carbon\Carbon;
@ -10,9 +11,13 @@ class ThrottlingTest extends \MailPoetTest {
/** @var Throttling */ /** @var Throttling */
private $throttling; private $throttling;
/** @var SubscriberIPsRepository */
private $subscriberIPsRepository;
protected function _before() { protected function _before() {
parent::_before(); parent::_before();
$this->throttling = $this->diContainer->get(Throttling::class); $this->throttling = $this->diContainer->get(Throttling::class);
$this->subscriberIPsRepository = $this->diContainer->get(SubscriberIPsRepository::class);
} }
public function testItProgressivelyThrottlesSubscriptions() { public function testItProgressivelyThrottlesSubscriptions() {
@ -20,10 +25,7 @@ class ThrottlingTest extends \MailPoetTest {
expect($this->throttling->throttle())->equals(false); expect($this->throttling->throttle())->equals(false);
expect($this->throttling->throttle())->equals(60); expect($this->throttling->throttle())->equals(60);
for ($i = 1; $i <= 10; $i++) { for ($i = 1; $i <= 10; $i++) {
$ip = SubscriberIP::create(); $this->createSubscriberIP('127.0.0.1', Carbon::now()->subMinutes($i));
$ip->ip = '127.0.0.1';
$ip->createdAt = Carbon::now()->subMinutes($i);
$ip->save();
} }
expect($this->throttling->throttle())->equals(MINUTE_IN_SECONDS * pow(2, 10)); expect($this->throttling->throttle())->equals(MINUTE_IN_SECONDS * pow(2, 10));
} }
@ -49,18 +51,12 @@ class ThrottlingTest extends \MailPoetTest {
} }
public function testItPurgesOldSubscriberIps() { public function testItPurgesOldSubscriberIps() {
$ip = SubscriberIP::create(); $this->createSubscriberIP('127.0.0.1', Carbon::now());
$ip->ip = '127.0.0.1'; $this->createSubscriberIP('127.0.0.1', Carbon::now()->subDays(30)->subSeconds(1));
$ip->save();
$ip2 = SubscriberIP::create(); expect($this->subscriberIPsRepository->countBy([]))->equals(2);
$ip2->ip = '127.0.0.1';
$ip2->createdAt = Carbon::now()->subDays(30)->subSeconds(1);
$ip2->save();
expect(SubscriberIP::count())->equals(2);
$this->throttling->throttle(); $this->throttling->throttle();
expect(SubscriberIP::count())->equals(1); expect($this->subscriberIPsRepository->countBy([]))->equals(1);
} }
public function testItConvertsSecondsToTimeString() { public function testItConvertsSecondsToTimeString() {
@ -73,7 +69,15 @@ class ThrottlingTest extends \MailPoetTest {
expect($this->throttling->secondsToTimeString(59))->equals('59 seconds'); expect($this->throttling->secondsToTimeString(59))->equals('59 seconds');
} }
private function createSubscriberIP(string $ip, Carbon $createdAt): SubscriberIPEntity {
$subscriberIP = new SubscriberIPEntity($ip);
$subscriberIP->setCreatedAt($createdAt);
$this->entityManager->persist($subscriberIP);
$this->entityManager->flush();
return $subscriberIP;
}
public function _after() { public function _after() {
SubscriberIP::deleteMany(); $this->truncateEntity(SubscriberIPEntity::class);
} }
} }