| @@ -3,9 +3,11 @@ | ||
| 3 | 3 | namespace FluentCommunity\App\Hooks\Handlers; |
| 4 | 4 | |
| 5 | 5 | use FluentCommunity\App\Models\Comment; |
| 6 | 6 | use FluentCommunity\App\Models\Feed; |
| 7 | +use FluentCommunity\App\Models\Media; | |
| 7 | 8 | use FluentCommunity\App\Models\User; |
| 9 | +use FluentCommunity\App\Services\Helper; | |
| 8 | 10 | |
| 9 | 11 | class RateLimitHandler |
| 10 | 12 | { |
| 11 | 13 | public function register() |
| @@ -11,12 +13,18 @@ | ||
| 11 | 13 | public function register() |
| 12 | 14 | { |
| 13 | 15 | add_action('fluent_community/check_rate_limit/create_post', [$this, 'maybeLimitPost'], 10, 1); |
| 14 | 16 | add_action('fluent_community/check_rate_limit/create_comment', [$this, 'maybeLimitComment'], 10, 1); |
| 17 | + add_action('fluent_community/check_rate_limit/media_upload', [$this, 'maybeLimitMediaUpload'], 10, 1); | |
| 18 | + add_action('fluent_community/check_rate_limit/oembed', [$this, 'maybeLimitOembed'], 10, 1); | |
| 15 | 19 | } |
| 16 | 20 | |
| 17 | 21 | public function maybeLimitPost(User $user) |
| 18 | 22 | { |
| 23 | + if (Helper::isSiteAdmin($user->ID, $user)) { | |
| 24 | + return; | |
| 25 | + } | |
| 26 | + | |
| 19 | 27 | // Check how many posts user has created in last 5 minutes |
| 20 | 28 | $postsCount = Feed::query()->withoutGlobalScopes()->where('user_id', $user->ID) |
| 21 | 29 | ->where('created_at', '>', gmdate('Y-m-d H:i:s', current_time('timestamp') - 300)) |
| 22 | 30 | ->count(); |
| @@ -22,9 +30,9 @@ | ||
| 22 | 30 | ->count(); |
| 23 | 31 | |
| 24 | 32 | $limitPer5Minutes = apply_filters('fluent_community/rate_limit/posts_per_5_minutes', 5); |
| 25 | 33 | |
| 26 | - if ($postsCount > $limitPer5Minutes) { | |
| 34 | + if ($postsCount >= $limitPer5Minutes) { | |
| 27 | 35 | throw new \Exception(esc_html__('You have reached the limit of posting. Please try after some time', 'fluent-community')); |
| 28 | 36 | } |
| 29 | 37 | } |
| 30 | 38 | |
| @@ -29,9 +37,13 @@ | ||
| 29 | 37 | } |
| 30 | 38 | |
| 31 | 39 | public function maybeLimitComment(User $user) |
| 32 | 40 | { |
| 33 | - // Check how many comments user has created in last 5 minutes | |
| 41 | + if (Helper::isSiteAdmin($user->ID, $user)) { | |
| 42 | + return; | |
| 43 | + } | |
| 44 | + | |
| 45 | + // Check how many comments user has created in last 1 minute | |
| 34 | 46 | $commentsCount = Comment::query()->withoutGlobalScopes()->where('user_id', $user->ID) |
| 35 | 47 | ->where('created_at', '>', gmdate('Y-m-d H:i:s', current_time('timestamp') - 60)) |
| 36 | 48 | ->count(); |
| 37 | 49 | |
| @@ -36,9 +48,52 @@ | ||
| 36 | 48 | ->count(); |
| 37 | 49 | |
| 38 | 50 | $limitPerMinute = apply_filters('fluent_community/rate_limit/comments_per_minute', 5); |
| 39 | 51 | |
| 40 | - if ($commentsCount > $limitPerMinute) { | |
| 52 | + if ($commentsCount >= $limitPerMinute) { | |
| 41 | 53 | throw new \Exception(esc_html__('You have reached the limit of commenting. Please try after some time', 'fluent-community')); |
| 54 | + } | |
| 55 | + } | |
| 56 | + | |
| 57 | + public function maybeLimitMediaUpload(User $user) | |
| 58 | + { | |
| 59 | + if (Helper::isSiteAdmin($user->ID, $user)) { | |
| 60 | + return; | |
| 61 | + } | |
| 62 | + | |
| 63 | + // Check how many media items user has uploaded in last 1 minute | |
| 64 | + $mediaCount = Media::query()->withoutGlobalScopes()->where('user_id', $user->ID) | |
| 65 | + ->where('created_at', '>', gmdate('Y-m-d H:i:s', current_time('timestamp') - 60)) | |
| 66 | + ->count(); | |
| 67 | + | |
| 68 | + $limitPerMinute = apply_filters('fluent_community/rate_limit/media_upload_per_minute', 10); | |
| 69 | + | |
| 70 | + if ($mediaCount >= $limitPerMinute) { | |
| 71 | + throw new \Exception(esc_html__('You have reached the limit of media uploads. Please try after some time', 'fluent-community')); | |
| 72 | + } | |
| 73 | + } | |
| 74 | + | |
| 75 | + public function maybeLimitOembed(User $user) | |
| 76 | + { | |
| 77 | + if (Helper::isSiteAdmin($user->ID, $user)) { | |
| 78 | + return; | |
| 79 | + } | |
| 80 | + | |
| 81 | + // Without a persistent object cache the per-user counter would pile up as | |
| 82 | + // rows in wp_options, so the limit is only enforced when one is present. | |
| 83 | + if (!wp_using_ext_object_cache()) { | |
| 84 | + return; | |
| 85 | + } | |
| 86 | + | |
| 87 | + $limitPerMinute = apply_filters('fluent_community/rate_limit/oembed_per_minute', 20); | |
| 88 | + | |
| 89 | + // Transients rather than wp_cache_incr(): some managed hosts run Redis behind an | |
| 90 | + // ACL that denies INCRBY, and the Redis Object Cache drop-in dies on that error. | |
| 91 | + $transientKey = 'fcom_oembed_rate_limit_' . $user->ID; | |
| 92 | + $previewCount = (int) get_transient($transientKey) + 1; | |
| 93 | + set_transient($transientKey, $previewCount, MINUTE_IN_SECONDS); | |
| 94 | + | |
| 95 | + if ($previewCount > $limitPerMinute) { | |
| 96 | + throw new \Exception(esc_html__('You have reached the limit of link previews. Please try after some time', 'fluent-community')); | |
| 42 | 97 | } |
| 43 | 98 | } |
| 44 | 99 | } |