fix: /api/score と学習記録APIにレート制限・乱用対策を導入する#280
Open
keisato848 wants to merge 1 commit into
Open
Conversation
- apps/web/lib/rate-limit.ts を新規作成(スライディングウィンドウ方式) - /api/score: 認証ユーザー10回/5分、ゲスト3回/5分の制限を追加 - /api/learning-records POST: 認証ユーザー120回/分の制限を追加 - AIAnswerBox.tsx: 429 レスポンス時に分単位の再試行メッセージを表示 - catch(err: any) → catch(err: unknown) で型安全性も改善 - テスト: next-auth モックを追加し全テストパスを確認
Contributor
There was a problem hiding this comment.
Pull request overview
Issue #270 対応として、AI採点 API(/api/score)と学習記録 API(/api/learning-records)にレート制限を導入し、429 時のユーザー体験(UIメッセージ)とテストの安定化を図るPRです。
Changes:
- インメモリのレートリミッター(
checkRateLimit/ IP取得 / 制限定数)を新規追加 /api/scoreと/api/learning-recordsにレート制限チェックと 429 応答(Retry-After等)を追加- フロント側で 429 を明示的に扱い、エラーハンドリングの型安全性を改善
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/web/lib/rate-limit.ts | インメモリレートリミッター実装と制限値定義を追加 |
| apps/web/app/api/score/route.ts | セッション/IPベースで /api/score のレート制限と 429 応答を追加 |
| apps/web/app/api/learning-records/route.ts | 学習記録POSTにユーザーIDベースのレート制限と 429 応答を追加 |
| apps/web/components/features/exam/AIAnswerBox.tsx | 429時のUIメッセージ表示と catch の unknown 化 |
| apps/web/tests/api/score.test.ts | getServerSession をゲスト固定でモックしてテストを安定化 |
Comment on lines
+19
to
+25
| function maybeCleanup(windowMs: number): void { | ||
| const now = Date.now(); | ||
| if (now - lastCleanup < CLEANUP_INTERVAL_MS) return; | ||
| lastCleanup = now; | ||
| for (const [key, entry] of store.entries()) { | ||
| if (now - entry.windowStart > windowMs * 2) { | ||
| store.delete(key); |
Comment on lines
+39
to
+43
| /** | ||
| * レート制限チェック(スライディングウィンドウ方式) | ||
| * | ||
| * @param key レート制限キー(例: `score:user:xxx`) | ||
| * @param maxRequests ウィンドウ内の最大リクエスト数 |
| const ip = getClientIp(req.headers); | ||
| const rateLimitKey = userId ? `score:user:${userId}` : `score:ip:${ip}`; | ||
| const limit = userId ? RATE_LIMITS.SCORE_AUTH : RATE_LIMITS.SCORE_GUEST; | ||
| const { allowed, remaining, retryAfter } = checkRateLimit(rateLimitKey, limit.maxRequests, limit.windowMs); |
Comment on lines
+94
to
+100
| // 学習記録の過剰書き込み防止 | ||
| const rateLimitKey = `learning-records:user:${session.user.id}`; | ||
| const { allowed, retryAfter } = checkRateLimit( | ||
| rateLimitKey, | ||
| RATE_LIMITS.LEARNING_RECORDS.maxRequests, | ||
| RATE_LIMITS.LEARNING_RECORDS.windowMs, | ||
| ); |
Comment on lines
+46
to
+50
| export function checkRateLimit( | ||
| key: string, | ||
| maxRequests: number, | ||
| windowMs: number, | ||
| ): RateLimitResult { |
✅ Staging デプロイ完了
|
6ca281d to
1fbb691
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
Issue #270 対応。AI採点API (/api/score) および学習記録API (/api/learning-records) へのレート制限を実装する。
変更内容
新規ファイル
修正ファイル
制限値
受け入れ基準チェック
テスト