fixed up ratelimiting

This commit is contained in:
Boki 2025-07-06 18:53:02 -04:00
parent a616c92656
commit a7146a3f57
15 changed files with 912 additions and 186 deletions

View file

@ -126,6 +126,7 @@ export abstract class BaseHandler<TServices extends ServiceTypes = ServiceTypes>
if (!this.queue) {
throw new Error('Queue service is not available for this handler');
}
const jobData = {
handler: this.handlerName,
operation,

View file

@ -1,12 +1,30 @@
/**
* Individual rate limit window configuration
*/
export interface RateLimitWindow {
points: number; // Number of allowed points in the time window
duration: number; // Time window in seconds
blockDuration?: number; // How long to block after limit is hit (seconds)
}
/**
* Rate limit configuration for handlers and operations
*/
export interface RateLimitConfig {
points: number; // Number of allowed requests
duration: number; // Time window in seconds
blockDuration?: number; // How long to block after limit is hit (seconds)
limits?: RateLimitWindow[]; // Array of rate limit windows
cost?: number; // How many points this operation costs (default: 1)
}
/**
* Flexible rate limit configuration
* Can be:
* - A full config object with limits and cost
* - Just a cost number (for operations inheriting handler limits)
* - An array of limit windows (legacy support)
* - A single limit window (legacy support)
*/
export type RateLimitOptions = RateLimitConfig | number | RateLimitWindow[] | RateLimitWindow;
/**
* RateLimit decorator - configures rate limiting for handlers or operations
*
@ -14,26 +32,62 @@ export interface RateLimitConfig {
* - Classes: Sets default rate limit for all operations in the handler
* - Methods: Sets specific rate limit for individual operations (overrides handler-level)
*
* @param config Rate limit configuration
* @param options Rate limit configuration - flexible format
*
* @example
* // Handler-level rate limit
* // Handler-level rate limit with multiple windows
* @Handler('myHandler')
* @RateLimit({ points: 100, duration: 60 })
* @RateLimit({
* limits: [
* { points: 100, duration: 1 }, // 100 points/second
* { points: 10000, duration: 3600 } // 10k points/hour
* ],
* cost: 1 // Default cost for all operations
* })
* class MyHandler extends BaseHandler {
* // All operations inherit the 100/minute limit
* // All operations inherit these limits
* }
*
* @example
* // Operation-specific rate limit
* @Operation('fetch-data')
* @RateLimit({ points: 10, duration: 60, blockDuration: 30 })
* async fetchData() {
* // This operation is limited to 10/minute
* }
* // Operation-specific cost only (inherits handler limits)
* @Operation('fetch-simple')
* @RateLimit(1) // Costs 1 point
* async fetchSimple() {}
*
* @Operation('fetch-complex')
* @RateLimit({ cost: 10 }) // Costs 10 points
* async fetchComplex() {}
*
* @example
* // Operation with custom limits and cost
* @Operation('special-operation')
* @RateLimit({
* limits: [
* { points: 10, duration: 60 } // More restrictive: 10/minute
* ],
* cost: 5
* })
* async specialOperation() {}
*/
export function RateLimit(config: RateLimitConfig) {
export function RateLimit(options: RateLimitOptions) {
return function (target: any, propertyKey?: string, descriptor?: PropertyDescriptor): any {
// Normalize options to RateLimitConfig format
let config: RateLimitConfig;
if (typeof options === 'number') {
// Just a cost number
config = { cost: options };
} else if (Array.isArray(options)) {
// Array of limit windows (legacy support)
config = { limits: options };
} else if ('points' in options && 'duration' in options) {
// Single limit window (legacy support)
config = { limits: [options as RateLimitWindow] };
} else {
// Already a RateLimitConfig
config = options as RateLimitConfig;
}
if (propertyKey) {
// Method decorator - operation-specific rate limit
const constructor = target.constructor;

View file

@ -8,7 +8,11 @@ export {
Disabled,
} from './decorators/decorators';
export { RateLimit } from './decorators/rate-limit';
export type { RateLimitConfig } from './decorators/rate-limit';
export type {
RateLimitConfig,
RateLimitWindow,
RateLimitOptions
} from './decorators/rate-limit';
export { createJobHandler } from './utils/create-job-handler';
// Re-export commonly used types from @stock-bot/types for convenience