Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

Build the rate limiter on the new CounterStore, so limits hold across server instances

已關閉
#245 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

維護者通常 1 天內回覆

@abelzach 已經在處理了。

開始於 2026年10月21日。

  • #274 來自 @abelzach —— 已合併

評估

難度
3/5
預估耗時
1-2 天
新手友好度
83/100
Issue 類型
功能
描述清晰度
描述清楚
活躍度
活躍
技術堆疊
node.js, typescript
領域
backend, databases

研究方向

Read packages/core/src/storage/types.ts for the CounterStore contract, then review packages/server/src/rate-limit.ts and its tests for existing behavior. Implement the algorithm in packages/core/src/limits/rate-limit.ts and add fake-clock and concurrency coverage in packages/core/tests/limits/rate-limit.test.ts, using the in-memory counter store. Done means the listed acceptance criteria pass and the core package exports the limiter with a minor changeset.

由索引模型根據 Issue 內容生成。

描述

core enhancement hacktoberfest help wanted

The server's rate limiter keeps its counts in a Map in the process (packages/server/src/rate-limit.ts). On Vercel or Workers every instance has its own map, so a visitor spread over five instances gets five times the limit. It also needs a setInterval for cleanup (packages/server/src/handler.ts, line 35), and Workers don't allow timers at global scope.

@chatcops/core now has a CounterStore interface (packages/core/src/storage/types.ts): atomic counters that reset after a while, backed by Redis, Postgres and so on. This issue is the rate limit algorithm on top of it. Wiring it into the server comes in a later PR.

What to do
Create packages/core/src/limits/rate-limit.ts:

export interface RateLimitRule {
  limit: number;      // requests allowed per window
  windowMs: number;
}

export interface RateLimitResult {
  allowed: boolean;
  /** Requests left in the current window, never below 0. */
  remaining: number;
  /** When denied: how long until a request would be allowed again. */
  retryAfterMs?: number;
}

export function createRateLimiter(counters: CounterStore, rule: RateLimitRule, options?: { prefix?: string; now?: () => number }): {
  check(key: string): Promise<RateLimitResult>;
};

Use a sliding window built from two fixed windows. It's smooth at window edges (no burst of 2x the limit at the boundary) and needs only two counters:

  • The window index is Math.floor(now / windowMs). Count this request with counters.increment(${prefix}:${key}:${index}, 1, 2 * windowMs), and read the previous window's counter with get.
  • The estimated count is previous * (1 - elapsedInWindow / windowMs) + current. Allow the request when it's at most limit.
  • Denied requests still count, so a client that keeps hammering stays limited.
  • retryAfterMs is an estimate: the time until the estimate drops back under the limit, at least 1 ms and at most windowMs. Explain the formula in a comment.
  • prefix defaults to 'rl'. Validate limit > 0 and windowMs > 0 like RateLimiter does.

Where to look

  • packages/server/src/rate-limit.ts (today's limiter and its tests in packages/server/tests/rate-limit.test.ts)
  • packages/core/src/storage/memory.ts (memoryStorage().counters, use it in the tests with a fake now)
  • Cloudflare's write-up of the same sliding window idea

Acceptance criteria

  • New packages/core/src/limits/rate-limit.ts and packages/core/tests/limits/rate-limit.test.ts, exported from @chatcops/core
  • Tests with a fake clock: the first limit requests pass and the next one doesn't; the count decays across the window boundary (half the previous window still counts at the midpoint); keys don't affect each other; retryAfterMs is set when denied and within bounds
  • A test with 30 concurrent check() calls against a limit of 10 allows exactly 10
  • Changeset for @chatcops/core (minor)
  • pnpm test:coverage and pnpm lint pass

Want to work on this? Comment below and I'll assign it to you. Please take one issue at a time, branch off dev, and open your PR against dev with Closes #<this issue number>. Setup notes (pnpm, build before test) are in CONTRIBUTING.md.

主要語言
TypeScript
星號
13
分支
7
平均合併
3 小時 28 分鐘
30 天內合併 PR
182

環境準備

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

codercops/chatcops 的其他 Issue

查看 codercops/chatcops 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。