@adonisjs/limiter
Version:
Rate limiting package for AdonisJS framework
224 lines (223 loc) • 7.53 kB
TypeScript
import { ConfigProvider } from '@adonisjs/core/types';
import { LimiterManager } from './limiter_manager.js';
import type { LimiterResponse } from './response.js';
/**
* The base configuration shared across all the stores.
*
* These options are inherited from the "rate-limiter-flexible"
* package. However, a custom store can ignore these options
* and create the implementation from scratch with custom
* set of options.
*/
export type LimiterStoreBaseConfig = {
/**
* The prefix to apply to all keys to ensure they are
* unique across different limiter instances.
*
* Defaults to the key of the stores collection.
*/
keyPrefix?: string;
/**
* Define the number of requests after which the key should
* be blocked within memory and avoid hitting the store.
*
* Let's understand this with an example:
* - You allow 100 requests per minute to a user
* - They make 140 requests. The last 40 requests will be denied
* - However, the store still has to consult the database to know
* if there are any requests left for a user on a given key.
* - With this option, you can tell the store to stop consulting
* the database after the count reaches 120.
*/
inMemoryBlockOnConsumed?: number;
/**
* The duration for which to block the user within memory after the
* user has consumed all the requests. The value of this property
* must match the "blockDuration" property in most case.
*
* The value must be a number in seconds or a string expression
*/
inMemoryBlockDuration?: number | string;
/**
* Delay the subsequent requests, so that all requests finish at the
* end of the duration timeframe.
*
* Let's understand with an example
*
* - You allow a user to make 10 requests every 5 mins.
* - Now, if they make all 10 requests within the first minute, they
* will be sitting idle for next 4 mins.
* - Now multiply this behavior across all the users of your app and
* therefore you might see a peak in traffic during the first min
* but no traffic in the last 4 mins.
*
*
* - With "execEvenly" enabled, if a user makes 10 requests within the
* first minute, they all will be kept waiting with incremental
* timeouts.
* - Hence, the last request made during that 1st minute will finish
* after 5mins.
*
* Learn more
* https://github.com/animir/node-rate-limiter-flexible/wiki/Smooth-out-traffic-peaks
*/
execEvenly?: boolean;
};
/**
* The options accepted by stores to consume request/points
* for a given key.
*/
export type LimiterConsumptionOptions = Pick<LimiterStoreBaseConfig, 'inMemoryBlockDuration' | 'inMemoryBlockOnConsumed'> & {
/**
* Number of requests to allow during the specific
* duration
*/
requests: number;
/**
* The duration after which the requests will be reset.
*
* The value must be a number in seconds or a string expression.
*/
duration: number | string;
/**
* The duration for which the key will be blocked after
* consuming all the requests.
*
* The blocking should be performed when you want to penalize
* a user for consuming all the requests.
*
* The value must be a number in seconds or a string expression
*/
blockDuration?: number | string;
};
/**
* Config accepted by the limiter's memory store
*/
export type LimiterMemoryStoreConfig = LimiterStoreBaseConfig & LimiterConsumptionOptions;
/**
* Config accepted by the limiter's redis store
*/
export type LimiterRedisStoreConfig = LimiterStoreBaseConfig & LimiterConsumptionOptions & {
/**
* Reject limiter instance creation when redis is not
* ready to accept connection
*/
rejectIfRedisNotReady?: boolean;
};
/**
* Config accepted by the limiter's database store
*/
export type LimiterDatabaseStoreConfig = LimiterStoreBaseConfig & LimiterConsumptionOptions & {
/**
* The database to connect with.
*/
dbName?: string;
/**
* The database table to use for storing keys. Defaults
* to "keyPrefix"
*/
tableName: string;
/**
* Define schema to use for making database queries.
*
* Applicable for postgres only
*/
schemaName?: string;
/**
* Automatically clear expired keys every 5 minutes.
*/
clearExpiredByTimeout?: boolean;
};
/**
* The limiter store contract that all stores should
* implement.
*/
export interface LimiterStoreContract {
/**
* A unique name for the store
*/
readonly name: string;
/**
* The number of configured requests on the store
*/
readonly requests: number;
/**
* The duration (in seconds) for which the requests are configured
*/
readonly duration: number;
/**
* The duration (in seconds) for which to block the key
*/
readonly blockDuration: number;
/**
* Consume 1 request for a given key. An exception is raised
* when all the requests have already been consumed or if
* the key is blocked.
*/
consume(key: string | number): Promise<LimiterResponse>;
/**
* Increment the number of consumed requests for a given key.
* No errors are thrown when limit has reached
*/
increment(key: string | number): Promise<LimiterResponse>;
/**
* Decrement the number of consumed requests for a given key.
*/
decrement(key: string | number): Promise<LimiterResponse>;
/**
* Block a given key for the given duration. The duration must be
* a value in seconds or a string expression.
*/
block(key: string | number, duration: string | number): Promise<LimiterResponse>;
/**
* Manually set the number of requests exhausted for
* a given key for the given time duration.
*
* For example: "ip_127.0.0.1" has made "20 requests" in "1 minute".
* Now, if you allow 25 requests in 1 minute, then only 5 requests
* are left.
*
* The duration must be a value in seconds or a string expression.
*/
set(key: string | number, requests: number, duration?: string | number): Promise<LimiterResponse>;
/**
* Delete a given key
*/
delete(key: string | number): Promise<boolean>;
/**
* Delete all keys blocked within the memory
*/
deleteInMemoryBlockedKeys?(): void;
/**
* Clear the storage database
*/
clear(): Promise<void>;
/**
* Get limiter response for a given key. Returns null when
* key doesn't exist.
*/
get(key: string | number): Promise<LimiterResponse | null>;
}
/**
* The manager factory is used to create an instance of the
* store with consumption options
*/
export type LimiterManagerStoreFactory = (options: LimiterConsumptionOptions) => LimiterStoreContract;
/**
* A list of known limiters inferred from the user config
*/
export interface LimitersList {
}
/**
* Helper method to resolve configured limiters
* inside user app
*/
export type InferLimiters<T extends ConfigProvider<{
stores: Record<string, LimiterManagerStoreFactory>;
}>> = Awaited<ReturnType<T['resolver']>>['stores'];
/**
* Limiter service is a singleton instance of limiter
* manager configured using user app's config
*/
export interface LimiterService extends LimiterManager<LimitersList extends Record<string, LimiterManagerStoreFactory> ? LimitersList : never> {
}