RESAZIP-PC\resaz

update ratelimit daitogooglechat

...@@ -126,6 +126,10 @@ This creates `config/daito-google-chat.php`. ...@@ -126,6 +126,10 @@ This creates `config/daito-google-chat.php`.
126 ```dotenv 126 ```dotenv
127 DAITO_GOOGLE_CHAT_ENABLED=true 127 DAITO_GOOGLE_CHAT_ENABLED=true
128 DAITO_GOOGLE_CHAT_WEBHOOK_URL=https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=... 128 DAITO_GOOGLE_CHAT_WEBHOOK_URL=https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=...
129 +DAITO_GOOGLE_CHAT_QUEUE_NAME=google-chat
130 +DAITO_GOOGLE_CHAT_RATE_LIMIT_ENABLED=true
131 +DAITO_GOOGLE_CHAT_RATE_LIMIT_MAX_JOBS=20
132 +DAITO_GOOGLE_CHAT_RATE_LIMIT_DECAY_SECONDS=60
129 ``` 133 ```
130 134
131 ### 3) Send immediately 135 ### 3) Send immediately
...@@ -195,6 +199,41 @@ DaitoGoogleChat::sendCardV2($arrCardV2); ...@@ -195,6 +199,41 @@ DaitoGoogleChat::sendCardV2($arrCardV2);
195 DaitoGoogleChat::queueCardV2($arrCardV2); 199 DaitoGoogleChat::queueCardV2($arrCardV2);
196 ``` 200 ```
197 201
202 +### 8) Anti-spam notes (recommended for production)
203 +
204 +- Queue job is rate-limited globally by cache key (`daito-google-chat:webhook`).
205 +- Default limit is `20` jobs / `60` seconds (configurable).
206 +- Keep `queue_backoff_seconds` as-is to retry safely on transient errors.
207 +
208 +Main config keys in `config/daito-google-chat.php`:
209 +
210 +- `rate_limit_enabled`
211 +- `rate_limit_max_jobs`
212 +- `rate_limit_decay_seconds`
213 +- `rate_limit_key`
214 +- `queue_name` (default: `google-chat`)
215 +
216 +### 9) Run dedicated queue worker for google-chat
217 +
218 +Run a separate worker to isolate notification throughput:
219 +
220 +```bash
221 +php artisan queue:work --queue=google-chat --sleep=1 --tries=3 --timeout=30
222 +```
223 +
224 +Supervisor example:
225 +
226 +```ini
227 +[program:laravel-google-chat-worker]
228 +process_name=%(program_name)s_%(process_num)02d
229 +command=php /path/to/artisan queue:work --queue=google-chat --sleep=1 --tries=3 --timeout=30
230 +autostart=true
231 +autorestart=true
232 +numprocs=1
233 +redirect_stderr=true
234 +stdout_logfile=/var/log/supervisor/laravel-google-chat-worker.log
235 +```
236 +
198 ## QueryLog (Laravel shared package) 237 ## QueryLog (Laravel shared package)
199 238
200 > **Provider registration note** 239 > **Provider registration note**
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
2 2
3 namespace Daito\Lib\DaitoGoogleChat\Jobs; 3 namespace Daito\Lib\DaitoGoogleChat\Jobs;
4 4
5 +use Daito\Lib\DaitoGoogleChat\Middleware\DaitoGoogleChatRateLimitMiddleware;
5 use Daito\Lib\DaitoGoogleChat\Services\DaitoGoogleChatWebhookClient; 6 use Daito\Lib\DaitoGoogleChat\Services\DaitoGoogleChatWebhookClient;
6 use Illuminate\Bus\Queueable; 7 use Illuminate\Bus\Queueable;
7 use Illuminate\Contracts\Queue\ShouldQueue; 8 use Illuminate\Contracts\Queue\ShouldQueue;
...@@ -31,4 +32,15 @@ class DaitoGoogleChatWebhookJob implements ShouldQueue ...@@ -31,4 +32,15 @@ class DaitoGoogleChatWebhookJob implements ShouldQueue
31 { 32 {
32 $client->send($this->arrPayload, $this->webhookUrl); 33 $client->send($this->arrPayload, $this->webhookUrl);
33 } 34 }
35 +
36 + public function middleware(): array
37 + {
38 + if (!(bool) config('daito-google-chat.rate_limit_enabled', true)) {
39 + return array();
40 + }
41 +
42 + return array(
43 + new DaitoGoogleChatRateLimitMiddleware(),
44 + );
45 + }
34 } 46 }
......
1 +<?php
2 +
3 +namespace Daito\Lib\DaitoGoogleChat\Middleware;
4 +
5 +use Illuminate\Support\Facades\RateLimiter;
6 +
7 +class DaitoGoogleChatRateLimitMiddleware
8 +{
9 + public function handle($job, $next): void
10 + {
11 + $rateLimitKey = (string) config('daito-google-chat.rate_limit_key', 'daito-google-chat:webhook');
12 + $maxJobs = max(1, (int) config('daito-google-chat.rate_limit_max_jobs', 20));
13 + $decaySeconds = max(1, (int) config('daito-google-chat.rate_limit_decay_seconds', 60));
14 +
15 + if (RateLimiter::tooManyAttempts($rateLimitKey, $maxJobs)) {
16 + $job->release(max(1, RateLimiter::availableIn($rateLimitKey)));
17 + return;
18 + }
19 +
20 + RateLimiter::hit($rateLimitKey, $decaySeconds);
21 + $next($job);
22 + }
23 +}
...@@ -12,8 +12,12 @@ return array( ...@@ -12,8 +12,12 @@ return array(
12 'retry_times' => env('DAITO_GOOGLE_CHAT_RETRY_TIMES', 1), 12 'retry_times' => env('DAITO_GOOGLE_CHAT_RETRY_TIMES', 1),
13 'retry_sleep_ms' => env('DAITO_GOOGLE_CHAT_RETRY_SLEEP_MS', 200), 13 'retry_sleep_ms' => env('DAITO_GOOGLE_CHAT_RETRY_SLEEP_MS', 200),
14 'queue_connection' => env('DAITO_GOOGLE_CHAT_QUEUE_CONNECTION', null), 14 'queue_connection' => env('DAITO_GOOGLE_CHAT_QUEUE_CONNECTION', null),
15 - 'queue_name' => env('DAITO_GOOGLE_CHAT_QUEUE_NAME', null), 15 + 'queue_name' => env('DAITO_GOOGLE_CHAT_QUEUE_NAME', 'google-chat'),
16 'queue_tries' => env('DAITO_GOOGLE_CHAT_QUEUE_TRIES', 3), 16 'queue_tries' => env('DAITO_GOOGLE_CHAT_QUEUE_TRIES', 3),
17 'queue_backoff_seconds' => env('DAITO_GOOGLE_CHAT_QUEUE_BACKOFF', 10), 17 'queue_backoff_seconds' => env('DAITO_GOOGLE_CHAT_QUEUE_BACKOFF', 10),
18 + 'rate_limit_enabled' => env('DAITO_GOOGLE_CHAT_RATE_LIMIT_ENABLED', true),
19 + 'rate_limit_max_jobs' => env('DAITO_GOOGLE_CHAT_RATE_LIMIT_MAX_JOBS', 20),
20 + 'rate_limit_decay_seconds' => env('DAITO_GOOGLE_CHAT_RATE_LIMIT_DECAY_SECONDS', 60),
21 + 'rate_limit_key' => env('DAITO_GOOGLE_CHAT_RATE_LIMIT_KEY', 'daito-google-chat:webhook'),
18 'throw_on_error' => env('DAITO_GOOGLE_CHAT_THROW_ON_ERROR', false), 22 'throw_on_error' => env('DAITO_GOOGLE_CHAT_THROW_ON_ERROR', false),
19 ); 23 );
......