diff --git a/cleantalkantispam.php b/cleantalkantispam.php index b1c6913..28f9cb7 100644 --- a/cleantalkantispam.php +++ b/cleantalkantispam.php @@ -58,6 +58,7 @@ define('APBCT_TBL_AC_LOG', 'cleantalk_ac_log'); // Table with firewall logs. define('APBCT_TBL_AC_UA_BL', 'cleantalk_ua_bl'); // Table with User-Agents blacklist. define('APBCT_TBL_SESSIONS', 'cleantalk_sessions'); // Table with session data. +define('APBCT_RATE_LIMITS', 'cleantalk_rate_limits'); // Table with different rate limits data. !defined('APBCT_TBL_STORAGE') && define('APBCT_TBL_STORAGE', 'cleantalk_custom_storage'); // Table with session data. define('APBCT_SFW_SEND_LOGS_LIMIT', 1000); define('APBCT_SPAMSCAN_LOGS', 'cleantalk_spamscan_logs'); // Table with session data. @@ -1645,13 +1646,13 @@ public function onAjaxCleantalkantispam() { return json_encode(['allow' => 1, 'msg' => '']); } - return ['error' => 'Not working']; + return json_encode(['error' => 'Not working']); default : - return ['error' => 'Wrong action was provided']; + return json_encode(['error' => 'Wrong action was provided']); } } - return ['error' => 'No action was provided']; + return json_encode(['error' => 'No action was provided']); } //////////////////////////// diff --git a/cleantalkantispam.xml b/cleantalkantispam.xml index 177cf8d..d74bc7a 100644 --- a/cleantalkantispam.xml +++ b/cleantalkantispam.xml @@ -7,7 +7,7 @@ GNU/GPLv2 welcome@cleantalk.org cleantalk.org - 3.2.6 + 3.3.0 PLG_SYSTEM_CLEANTALKANTISPAM_DESCRIPTION updater.php diff --git a/composer.json b/composer.json index de67488..729d4fd 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "cleantalk/api": "*", "cleantalk/cron": "*", "cleantalk/remote-calls": "*", - "cleantalk/storage-handler": "*" + "cleantalk/storage-handler": "*", + "cleantalk/rate-limiter": "*" }, "require-dev": { "phpunit/phpunit": "^8.5.52", diff --git a/lib/Cleantalk/Common/RateLimiter/RateLimiter.php b/lib/Cleantalk/Common/RateLimiter/RateLimiter.php new file mode 100644 index 0000000..2f83075 --- /dev/null +++ b/lib/Cleantalk/Common/RateLimiter/RateLimiter.php @@ -0,0 +1,227 @@ +config = $config; + $this->current_ts = time(); + + $this->setIP(); + $this->setUA(); + $this->setUID(); + } + + /** + * Sets the unique identifier for the current request + * Must be implemented by child classes + * + * @return void + */ + protected function setUID(): void + { + $this->uid = md5($this->ip . $this->ua . $this->config->type); + } + + /** + * Determines if the current request should be rate limited + * + * @return bool True if request should be allowed or error occurred, false if rate limited + */ + public function checkPassed(): bool + { + try { + if (!$this->healthCheck()) { + throw new \Exception('HEALTH_CHECK_FAILED'); + } + + if (!$this->cleanUp()) { + throw new \Exception('CLEANUP_FAILED'); + } + + $uid_data = $this->selectUIDData(); + $record_found = false !== $uid_data; + + if ($record_found && !$uid_data->data_ok) { + throw new \Exception('UID_DATA_INVALID'); + } + + if ($record_found && $this->isLocked($uid_data)) { + return false; // Block here by limit exceeded + } + + if ($record_found) { + if (!$this->increment($uid_data)) { + throw new \Exception('INCREMENT_FAILED'); + } + } else { + $uid_data = new RateLimiterDTO( + array( + 'uid' => $this->uid, + 'type' => $this->config->type, + 'counter' => 1, + 'last_call' => $this->current_ts, + 'created_at' => $this->current_ts, + 'ip' => $this->ip, + 'ua' => $this->ua, + ) + ); + if (!$uid_data->data_ok) { + throw new \Exception('UID_DATA_INVALID__INSERT'); + } + if (!$this->insert($uid_data)) { + throw new \Exception('INSERT_FAILED'); + } + } + } catch (\Exception $e) { + $this->process_ok = false; + $this->handleErrors($e->getMessage()); + return true; + } + + return true; + } + + /** + * Checks if the current UID has exceeded the rate limit + * + * @param RateLimiterDTO $uid_data + * @return bool True if rate limited, false otherwise + */ + protected function isLocked(RateLimiterDTO $uid_data): bool + { + return $uid_data->data_ok && ($uid_data->counter > $this->config->limit); + } + + /** + * Performs basic health check on configuration + * + * @return bool True if configuration is valid, false otherwise + */ + protected function healthCheck(): bool + { + if ($this->config->limit < 1) { + return false; + } + if ($this->config->period < 1) { + return false; + } + if ($this->config->type === null) { + return false; + } + return true; + } + + /** + * Retrieves rate limit data for the current UID + * Default implementation returns empty data + * + * @return RateLimiterDTO|false Rate limit data or false if not found + */ + protected function selectUIDData() + { + return new RateLimiterDTO(array()); + } + + /** + * Sets the IP address for the current request + * Must be implemented by child classes + * + * @return void + */ + abstract protected function setIP(): void; + + /** + * Sets the IP address for the current request + * Must be implemented by child classes + * + * @return void + */ + abstract protected function setUA(): void; + + /** + * Handles errors that occur during rate limiting + * Must be implemented by child classes + * + * @param string $msg Error message + * @return void + */ + abstract protected function handleErrors(string $msg): void; + + /** + * Increments the counter for an existing rate limit record + * Must be implemented by child classes + * @param RateLimiterDTO $uid_data + * @return bool True on success, false on failure + */ + abstract protected function increment(RateLimiterDTO $uid_data): bool; + + /** + * Inserts a new rate limit record + * Must be implemented by child classes + * @param RateLimiterDTO $uid_data + * @return bool True on success, false on failure + */ + abstract protected function insert(RateLimiterDTO $uid_data): bool; + + /** + * Cleans up expired rate limit records + * Must be implemented by child classes + * + * @return bool True on success, false on failure + */ + abstract protected function cleanUp(): bool; +} diff --git a/lib/Cleantalk/Common/RateLimiter/RateLimiterConfig.php b/lib/Cleantalk/Common/RateLimiter/RateLimiterConfig.php new file mode 100644 index 0000000..93f673b --- /dev/null +++ b/lib/Cleantalk/Common/RateLimiter/RateLimiterConfig.php @@ -0,0 +1,50 @@ +type = $type; + $this->limit = $limit; + $this->period = $period; + } +} diff --git a/lib/Cleantalk/Common/RateLimiter/RateLimiterDto.php b/lib/Cleantalk/Common/RateLimiter/RateLimiterDto.php new file mode 100644 index 0000000..59a8372 --- /dev/null +++ b/lib/Cleantalk/Common/RateLimiter/RateLimiterDto.php @@ -0,0 +1,94 @@ +data_ok = true; + } catch (\Exception $_e) { + $this->data_ok = false; + } + } +} diff --git a/lib/Cleantalk/Custom/AltCookies.php b/lib/Cleantalk/Custom/AltCookies.php index facc7aa..2b2e8ee 100644 --- a/lib/Cleantalk/Custom/AltCookies.php +++ b/lib/Cleantalk/Custom/AltCookies.php @@ -3,6 +3,8 @@ namespace Cleantalk\Custom; use Cleantalk\Common\Mloader\Mloader; +use Cleantalk\Common\RateLimiter\RateLimiterConfig; +use Cleantalk\Custom\RateLimiter\RateLimiter; use JFactory; class AltCookies @@ -11,6 +13,10 @@ class AltCookies private const SESSION_TABLE__NAME = 'cleantalk_sessions'; + private const LIMITER_NAME = 'alt_cookie_limit'; + + private const LIMITER_LIMIT = 10; // 10 requests per minute allowed + /** * @var string[] */ @@ -81,6 +87,13 @@ public static function get($name) public static function setFromRemote($data) { + $config = new RateLimiterConfig(self::LIMITER_NAME, self::LIMITER_LIMIT, 60); + $rate_limiter = new RateLimiter($config); + if ( ! $rate_limiter->checkPassed() ) { + http_response_code(403); + die(json_encode(['error' => 'LIMIT EXCEEDED'])); + } + $db = JFactory::getDbo(); $columns = array( 'id', diff --git a/lib/Cleantalk/Custom/RateLimiter/RateLimiter.php b/lib/Cleantalk/Custom/RateLimiter/RateLimiter.php new file mode 100644 index 0000000..be04476 --- /dev/null +++ b/lib/Cleantalk/Custom/RateLimiter/RateLimiter.php @@ -0,0 +1,151 @@ +db_object = $db_class::getInstance(); + $this->table_name = $this->db_object->prefix . APBCT_RATE_LIMITS; + } + + + /** + * @inheritDoc + */ + protected function setIP(): void + { + /** @var \Cleantalk\Common\Helper\Helper $helper_class */ + $helper_class = Mloader::get('Helper'); + $this->ip = $helper_class::ipGet(); + } + + /** + * @inheritDoc + */ + protected function setUA(): void + { + $this->ua = (string) Server::get('HTTP_USER_AGENT', 'default_ua'); + } + + /** + * @inheritDoc + */ + protected function handleErrors(string $msg): void + { + error_log('CleanTalk RateLimiter error: ' . $msg); + } + + /** + * @inheritDoc + */ + protected function increment(RateLimiterDto $uid_data): bool + { + $is_expired = ($this->current_ts - $uid_data->created_at) > $this->config->period; + + $uid_data->counter = $is_expired ? 1 : $uid_data->counter + 1; + $uid_data->created_at = $is_expired ? $this->current_ts : $uid_data->created_at; + $uid_data->last_call = $this->current_ts; + + $this->db_object->prepare( + ' + UPDATE ' . $this->table_name . ' SET + counter = %s, + last_call = %s, + created_at = %s + WHERE uid = %s + ', [ + $uid_data->counter, + $uid_data->last_call, + $uid_data->created_at, + $uid_data->uid + ]); + + $result = $this->db_object->execute($this->db_object->getQuery()); + + return false !== $result; + } + + /** + * @inheritDoc + */ + protected function insert(RateLimiterDto $uid_data): bool + { + $this->db_object->prepare( + ' + INSERT INTO ' . $this->table_name . ' + (uid, type, ip, ua, counter, last_call, created_at) + VALUES (%s, %s, %s, %s, 1, %s, %s) + ON DUPLICATE KEY UPDATE last_call = %s, counter = counter + 1; + ',[ + $uid_data->uid, + $uid_data->type, + $uid_data->ip, + $uid_data->ua, + $uid_data->last_call, + $uid_data->created_at, + $uid_data->last_call + ]); + + $result = $this->db_object->execute($this->db_object->getQuery()); + + return false !== $result; + } + + /** + * @inheritDoc + */ + protected function cleanUp(): bool + { + $threshold = $this->current_ts - ($this->config->period + 10); + + $this->db_object->prepare( + 'DELETE FROM ' . $this->table_name . ' WHERE created_at < %s AND type = %s;', + [ + $threshold, + $this->config->type + ] + ); + + $result = $this->db_object->execute($this->db_object->getQuery()); + + return false !== $result; + } + + /** + * Retrieves rate limit data for the current UID from database + * + * @return RateLimiterDTO|false Rate limit data object or false if not found + */ + public function selectUIDData() + { + $this->db_object->prepare( + ' + SELECT uid, type, ip, ua, counter, last_call, created_at FROM ' . $this->table_name . ' + WHERE uid = %s LIMIT 1; + ', + [$this->uid] + ); + $result = $this->db_object->fetch($this->db_object->getQuery()); + + return !empty($result) ? new RateLimiterDTO($result) : false; + } +} diff --git a/plugin-updates.xml b/plugin-updates.xml index c68c30e..2a96e61 100644 --- a/plugin-updates.xml +++ b/plugin-updates.xml @@ -6,15 +6,15 @@ cleantalkantispam plugin system - 3.2.6 + 3.3.0 - https://github.com/CleanTalk/joomla3.x-4.x-antispam/archive/3.2.6.zip + https://github.com/CleanTalk/joomla3.x-4.x-antispam/archive/refs/heads/Upd-Alt-cookie-rate-limit-VI.zip stable
Updates
- + site diff --git a/sql/mariadb/install.mariadb.utf8.sql b/sql/mariadb/install.mariadb.utf8.sql index 0865ba0..ba1b969 100644 --- a/sql/mariadb/install.mariadb.utf8.sql +++ b/sql/mariadb/install.mariadb.utf8.sql @@ -49,3 +49,13 @@ CREATE TABLE IF NOT EXISTS `#__cleantalk_custom_storage` ( `value` MEDIUMTEXT NULL DEFAULT NULL, PRIMARY KEY (`name`) ); +CREATE TABLE IF NOT EXISTS `#__cleantalk_rate_limits` ( + uid VARCHAR(32) NOT NULL, + type VARCHAR(32) NOT NULL, + ip VARCHAR(45) NOT NULL, + ua VARCHAR(200) NOT NULL, + counter INT NOT NULL DEFAULT 1, + last_call INT NOT NULL, + created_at INT NOT NULL, + PRIMARY KEY (uid) +); diff --git a/sql/mariadb/uninstall.mariadb.utf8.sql b/sql/mariadb/uninstall.mariadb.utf8.sql index 449f811..340b8cd 100644 --- a/sql/mariadb/uninstall.mariadb.utf8.sql +++ b/sql/mariadb/uninstall.mariadb.utf8.sql @@ -4,3 +4,4 @@ DROP TABLE IF EXISTS `#__cleantalk_sessions`; DROP TABLE IF EXISTS `#__cleantalk_ua_bl`; DROP TABLE IF EXISTS `#__cleantalk_usermeta`; DROP TABLE IF EXISTS `#__cleantalk_custom_storage`; +DROP TABLE IF EXISTS `#__cleantalk_rate_limits`; diff --git a/sql/mariadb/updates/3.3.0.sql b/sql/mariadb/updates/3.3.0.sql new file mode 100644 index 0000000..118877d --- /dev/null +++ b/sql/mariadb/updates/3.3.0.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS `#__cleantalk_usermeta` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `meta_key` varchar(255) DEFAULT NULL, + `meta_value` longtext DEFAULT NULL, + PRIMARY KEY (`id`) +); +CREATE TABLE IF NOT EXISTS `#__cleantalk_rate_limits` ( + uid VARCHAR(32) NOT NULL, + type VARCHAR(32) NOT NULL, + ip VARCHAR(45) NOT NULL, + ua VARCHAR(200) NOT NULL, + counter INT NOT NULL DEFAULT 1, + last_call INT NOT NULL, + created_at INT NOT NULL, + PRIMARY KEY (uid) +); diff --git a/sql/mariadb/updates/3.3.sql b/sql/mariadb/updates/3.3.sql deleted file mode 100644 index 13bdd1a..0000000 --- a/sql/mariadb/updates/3.3.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS `#__cleantalk_usermeta` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `meta_key` varchar(255) DEFAULT NULL, - `meta_value` longtext DEFAULT NULL, - PRIMARY KEY (`id`) -); \ No newline at end of file diff --git a/sql/mysql/install.mysql.utf8.sql b/sql/mysql/install.mysql.utf8.sql index 0327b48..e91d7d4 100644 --- a/sql/mysql/install.mysql.utf8.sql +++ b/sql/mysql/install.mysql.utf8.sql @@ -49,3 +49,13 @@ CREATE TABLE IF NOT EXISTS `#__cleantalk_custom_storage` ( `value` MEDIUMTEXT NULL DEFAULT NULL, PRIMARY KEY (`name`) ); +CREATE TABLE IF NOT EXISTS `#__cleantalk_rate_limits` ( + uid VARCHAR(32) NOT NULL, + type VARCHAR(32) NOT NULL, + ip VARCHAR(45) NOT NULL, + ua VARCHAR(200) NOT NULL, + counter INT NOT NULL DEFAULT 1, + last_call INT NOT NULL, + created_at INT NOT NULL, + PRIMARY KEY (uid) +); diff --git a/sql/mysql/uninstall.mysql.utf8.sql b/sql/mysql/uninstall.mysql.utf8.sql index 449f811..340b8cd 100644 --- a/sql/mysql/uninstall.mysql.utf8.sql +++ b/sql/mysql/uninstall.mysql.utf8.sql @@ -4,3 +4,4 @@ DROP TABLE IF EXISTS `#__cleantalk_sessions`; DROP TABLE IF EXISTS `#__cleantalk_ua_bl`; DROP TABLE IF EXISTS `#__cleantalk_usermeta`; DROP TABLE IF EXISTS `#__cleantalk_custom_storage`; +DROP TABLE IF EXISTS `#__cleantalk_rate_limits`; diff --git a/sql/mysql/updates/3.3.0.sql b/sql/mysql/updates/3.3.0.sql new file mode 100644 index 0000000..118877d --- /dev/null +++ b/sql/mysql/updates/3.3.0.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS `#__cleantalk_usermeta` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `meta_key` varchar(255) DEFAULT NULL, + `meta_value` longtext DEFAULT NULL, + PRIMARY KEY (`id`) +); +CREATE TABLE IF NOT EXISTS `#__cleantalk_rate_limits` ( + uid VARCHAR(32) NOT NULL, + type VARCHAR(32) NOT NULL, + ip VARCHAR(45) NOT NULL, + ua VARCHAR(200) NOT NULL, + counter INT NOT NULL DEFAULT 1, + last_call INT NOT NULL, + created_at INT NOT NULL, + PRIMARY KEY (uid) +); diff --git a/sql/mysql/updates/3.3.sql b/sql/mysql/updates/3.3.sql deleted file mode 100644 index 13bdd1a..0000000 --- a/sql/mysql/updates/3.3.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS `#__cleantalk_usermeta` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `meta_key` varchar(255) DEFAULT NULL, - `meta_value` longtext DEFAULT NULL, - PRIMARY KEY (`id`) -); \ No newline at end of file diff --git a/sql/sqlsrv/install.mysql.utf8.sql b/sql/sqlsrv/install.mysql.utf8.sql deleted file mode 100644 index 0865ba0..0000000 --- a/sql/sqlsrv/install.mysql.utf8.sql +++ /dev/null @@ -1,51 +0,0 @@ -CREATE TABLE IF NOT EXISTS `#__cleantalk_sfw` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `network` int(11) unsigned NOT NULL, - `mask` int(11) unsigned NOT NULL, - `status` tinyint(1) NOT NULL DEFAULT 0, - `source` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX ( `network` , `mask` ) -); -CREATE TABLE IF NOT EXISTS `#__cleantalk_sfw_logs` ( - `id` VARCHAR(40) NOT NULL, - `ip` VARCHAR(15) NOT NULL, - `status` ENUM('PASS_SFW','DENY_SFW','PASS_SFW__BY_WHITELIST','PASS_SFW__BY_COOKIE','DENY_ANTICRAWLER','PASS_ANTICRAWLER','DENY_ANTICRAWLER_UA','PASS_ANTICRAWLER_UA','DENY_ANTIFLOOD','PASS_ANTIFLOOD') NULL DEFAULT NULL, - `all_entries` INT NOT NULL, - `blocked_entries` INT NOT NULL, - `entries_timestamp` INT NOT NULL, - `ua_id` INT(11) NULL DEFAULT NULL, - `ua_name` VARCHAR(1024) NOT NULL, - `source` TINYINT NULL DEFAULT NULL, - `network` VARCHAR(20) NULL DEFAULT NULL, - `first_url`VARCHAR(100) NULL DEFAULT NULL, - `last_url` VARCHAR(100) NULL DEFAULT NULL, - PRIMARY KEY (`id`) -); -CREATE TABLE IF NOT EXISTS `#__cleantalk_sessions` ( - `id` varchar(64) NOT NULL, - `name` varchar(40) NOT NULL, - `value` text NULL DEFAULT NULL, - `last_update` datetime NULL DEFAULT NULL, - PRIMARY KEY (`name`(40), `id`(64)) -); -CREATE TABLE IF NOT EXISTS `#__cleantalk_ua_bl` ( - `id` int(11) NOT NULL, - `ua_template` varchar(255) DEFAULT NULL, - `ua_status` tinyint(4) DEFAULT NULL, - PRIMARY KEY (`id`) -); -UPDATE `#__extensions` SET params = '{"ct_check_register":1,"ct_check_contact_forms":1,"check_search":1,"ct_jcomments_check_comments":1,"roles_exclusions":"administrator,super users","ct_set_cookies":1}' -WHERE element = 'cleantalkantispam' AND folder = 'system'; -CREATE TABLE IF NOT EXISTS `#__cleantalk_usermeta` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `meta_key` varchar(255) DEFAULT NULL, - `meta_value` longtext DEFAULT NULL, - PRIMARY KEY (`id`) -); -CREATE TABLE IF NOT EXISTS `#__cleantalk_custom_storage` ( - `name` VARCHAR(100) NOT NULL DEFAULT '', - `value` MEDIUMTEXT NULL DEFAULT NULL, - PRIMARY KEY (`name`) -); diff --git a/sql/sqlsrv/install.sqlsrv.utf8.sql b/sql/sqlsrv/install.sqlsrv.utf8.sql index 0865ba0..ba1b969 100644 --- a/sql/sqlsrv/install.sqlsrv.utf8.sql +++ b/sql/sqlsrv/install.sqlsrv.utf8.sql @@ -49,3 +49,13 @@ CREATE TABLE IF NOT EXISTS `#__cleantalk_custom_storage` ( `value` MEDIUMTEXT NULL DEFAULT NULL, PRIMARY KEY (`name`) ); +CREATE TABLE IF NOT EXISTS `#__cleantalk_rate_limits` ( + uid VARCHAR(32) NOT NULL, + type VARCHAR(32) NOT NULL, + ip VARCHAR(45) NOT NULL, + ua VARCHAR(200) NOT NULL, + counter INT NOT NULL DEFAULT 1, + last_call INT NOT NULL, + created_at INT NOT NULL, + PRIMARY KEY (uid) +); diff --git a/sql/sqlsrv/uninstall.mysql.utf8.sql b/sql/sqlsrv/uninstall.mysql.utf8.sql deleted file mode 100644 index 449f811..0000000 --- a/sql/sqlsrv/uninstall.mysql.utf8.sql +++ /dev/null @@ -1,6 +0,0 @@ -DROP TABLE IF EXISTS `#__cleantalk_sfw`; -DROP TABLE IF EXISTS `#__cleantalk_sfw_logs`; -DROP TABLE IF EXISTS `#__cleantalk_sessions`; -DROP TABLE IF EXISTS `#__cleantalk_ua_bl`; -DROP TABLE IF EXISTS `#__cleantalk_usermeta`; -DROP TABLE IF EXISTS `#__cleantalk_custom_storage`; diff --git a/sql/sqlsrv/uninstall.sqlsrv.utf8.sql b/sql/sqlsrv/uninstall.sqlsrv.utf8.sql index 449f811..340b8cd 100644 --- a/sql/sqlsrv/uninstall.sqlsrv.utf8.sql +++ b/sql/sqlsrv/uninstall.sqlsrv.utf8.sql @@ -4,3 +4,4 @@ DROP TABLE IF EXISTS `#__cleantalk_sessions`; DROP TABLE IF EXISTS `#__cleantalk_ua_bl`; DROP TABLE IF EXISTS `#__cleantalk_usermeta`; DROP TABLE IF EXISTS `#__cleantalk_custom_storage`; +DROP TABLE IF EXISTS `#__cleantalk_rate_limits`; diff --git a/sql/sqlsrv/updates/3.3.0.sql b/sql/sqlsrv/updates/3.3.0.sql new file mode 100644 index 0000000..118877d --- /dev/null +++ b/sql/sqlsrv/updates/3.3.0.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS `#__cleantalk_usermeta` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `meta_key` varchar(255) DEFAULT NULL, + `meta_value` longtext DEFAULT NULL, + PRIMARY KEY (`id`) +); +CREATE TABLE IF NOT EXISTS `#__cleantalk_rate_limits` ( + uid VARCHAR(32) NOT NULL, + type VARCHAR(32) NOT NULL, + ip VARCHAR(45) NOT NULL, + ua VARCHAR(200) NOT NULL, + counter INT NOT NULL DEFAULT 1, + last_call INT NOT NULL, + created_at INT NOT NULL, + PRIMARY KEY (uid) +); diff --git a/sql/sqlsrv/updates/3.3.sql b/sql/sqlsrv/updates/3.3.sql deleted file mode 100644 index 13bdd1a..0000000 --- a/sql/sqlsrv/updates/3.3.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS `#__cleantalk_usermeta` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `meta_key` varchar(255) DEFAULT NULL, - `meta_value` longtext DEFAULT NULL, - PRIMARY KEY (`id`) -); \ No newline at end of file