Files
ccs/src/web-server/routes/cliproxy-stats-routes/shared.ts
T
Tam Nhu Tran 919be3c722 refactor: P5 god-file splits (test-backed, public API preserved)
Epic P5. Splits 4 test-backed god-files into focused submodules using the
Monster File Splitting methodology; each original file is now a thin barrel
re-exporting the full public surface, so consumers are unchanged.

- src/management/shared-manager.ts (1631 -> barrel 21 + 10 submodules,
  max 337 LOC): fs-helpers, symlink-helpers, plugin-metadata-normalizer,
  plugin-layout-internals, shared-dir-linker, project-context-sync,
  project-memory-sync, migrations, orchestrator, types.
- src/web-server/routes/cliproxy-stats-routes.ts (1216 -> barrel 23 + 8
  submodules, max 290 LOC): shared, quota-helpers, version-helpers,
  restart-route, quota-routes, error-log-routes, config-routes, router.
- src/commands/persist-command.ts (1071 -> barrel + 8 submodules, max 258):
  types, arg-parsing, secure-file, backup-rotation, secret-detection,
  receipt, help, handler.
- src/commands/cliproxy/quota-subcommand.ts (1130 -> barrel + 14 submodules,
  max 303): types, format-helpers, codex/claude-window-helpers, provider-runtime,
  handlers, per-provider sections.

Public API preserved verbatim (default + named exports). All P3 structured
logging and P4 typed errors preserved through the splits. Each resulting file
< 400 LOC. Existing tests (shared-manager x4, cliproxy-stats x4, persist x2,
quota-subcommand x1) green, unchanged.

Metric: files > 400 LOC 95 -> 91. validate + validate:ci-parity green.
2026-06-18 18:48:13 -04:00

71 lines
2.2 KiB
TypeScript

/**
* Shared internals for cliproxy-stats-routes: logger and rate-limit helpers.
*
* These are NOT part of the public barrel surface; they exist so the sibling
* submodules (quota helpers, version helpers, route handlers) can share the
* same logger instance and rate-limit state without re-declaring them.
*/
import type { Request } from 'express';
import { createLogger } from '../../../services/logging';
/**
* Shared logger for every cliproxy-stats submodule. Preserves the
* `web-server:routes:cliproxy-stats` channel used by P3 error conversions
* (`logger.error('stats.route.error', ...)`).
*/
export const logger = createLogger('web-server:routes:cliproxy-stats');
// ==================== Quota Rate Limiting ====================
export const QUOTA_RATE_LIMIT_WINDOW_MS = 60_000;
export const QUOTA_RATE_LIMIT_MAX_REQUESTS = 120;
export interface QuotaRateLimitEntry {
windowStart: number;
count: number;
}
/**
* In-memory rate limit state. Shared across all quota route handlers so that
* the same IP+provider key is throttled regardless of which handler runs.
*/
export const quotaRateLimits = new Map<string, QuotaRateLimitEntry>();
/**
* Build the rate-limit key for a request + provider pair.
*/
export function buildQuotaRateLimitKey(req: Request, provider: string): string {
const clientIp = req.ip || req.socket.remoteAddress || 'unknown';
return `${clientIp}:${provider}`;
}
/**
* Returns true when the caller should be rejected with 429.
* Evicts stale entries to prevent unbounded memory growth.
*/
export function isQuotaRouteRateLimited(req: Request, provider: string): boolean {
const key = buildQuotaRateLimitKey(req, provider);
const now = Date.now();
// Evict stale entries to prevent unbounded memory growth
if (quotaRateLimits.size > 1000) {
for (const [k, v] of quotaRateLimits) {
if (now - v.windowStart >= QUOTA_RATE_LIMIT_WINDOW_MS * 2) {
quotaRateLimits.delete(k);
}
}
}
const current = quotaRateLimits.get(key);
if (!current || now - current.windowStart >= QUOTA_RATE_LIMIT_WINDOW_MS) {
quotaRateLimits.set(key, { windowStart: now, count: 1 });
return false;
}
current.count += 1;
quotaRateLimits.set(key, current);
return current.count > QUOTA_RATE_LIMIT_MAX_REQUESTS;
}