feat: v4 — TUI fight loop, rate limiting, webhook tooling, expanded choreographies
- Fight loop CLI with TUI renderer (ink-style terminal UI) - Rate limiting middleware for API routes - Queue cooldowns wired into orchestrator after fights - Webhook test utility for bot debugging - API docs route - Expanded FightScene choreographies and weapon props - Fix Drizzle transaction execution in orchestrator - Schema additions, scoring/challenge/mock expansions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
2c0323d5fb
commit
4d8b18a58a
@@ -0,0 +1,51 @@
|
||||
import type { Context, Next } from 'hono'
|
||||
|
||||
const hitCounts = new Map<string, { count: number; resetAt: number }>()
|
||||
|
||||
// Cleanup stale entries every 5 minutes
|
||||
setInterval(() => {
|
||||
const now = Date.now()
|
||||
for (const [key, entry] of hitCounts) {
|
||||
if (now > entry.resetAt) hitCounts.delete(key)
|
||||
}
|
||||
}, 5 * 60 * 1000)
|
||||
|
||||
export function rateLimit(windowMs: number, maxHits: number) {
|
||||
return async (c: Context, next: Next) => {
|
||||
const key = c.req.header('x-forwarded-for') || c.req.header('cf-connecting-ip') || 'unknown'
|
||||
const now = Date.now()
|
||||
const entry = hitCounts.get(key)
|
||||
|
||||
if (!entry || now > entry.resetAt) {
|
||||
hitCounts.set(key, { count: 1, resetAt: now + windowMs })
|
||||
} else {
|
||||
entry.count++
|
||||
if (entry.count > maxHits) {
|
||||
return c.json({ error: 'Too many requests. Slow down.' }, 429)
|
||||
}
|
||||
}
|
||||
|
||||
await next()
|
||||
}
|
||||
}
|
||||
|
||||
// Per-bot rate limiter (uses bot ID instead of IP)
|
||||
const botHitCounts = new Map<string, number>()
|
||||
|
||||
export function botRateLimit(cooldownMs: number) {
|
||||
return async (c: Context, next: Next) => {
|
||||
const botId = c.req.param('botId')
|
||||
if (!botId) return next()
|
||||
|
||||
const lastHit = botHitCounts.get(botId) || 0
|
||||
const now = Date.now()
|
||||
|
||||
if (now - lastHit < cooldownMs) {
|
||||
const waitSec = Math.ceil((cooldownMs - (now - lastHit)) / 1000)
|
||||
return c.json({ error: `Cooldown active. Wait ${waitSec}s.` }, 429)
|
||||
}
|
||||
|
||||
botHitCounts.set(botId, now)
|
||||
await next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user