fixed up ratelimiting
This commit is contained in:
parent
a616c92656
commit
a7146a3f57
15 changed files with 912 additions and 186 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue