sveltekit-rate-limiter
Advanced tools
Comparing version 0.4.2 to 0.4.3
import type { Cookies, RequestEvent } from '@sveltejs/kit'; | ||
export type RateUnit = 'ms' | 's' | '15s' | '30s' | 'm' | '15m' | '30m' | 'h' | '2h' | '6h' | '12h' | 'd'; | ||
export type RateUnit = 'ms' | '100ms' | '250ms' | '500ms' | 's' | '2s' | '5s' | '10s' | '15s' | '30s' | '45s' | 'm' | '15m' | '30m' | 'h' | '2h' | '6h' | '12h' | 'd'; | ||
export type Rate = [number, RateUnit]; | ||
@@ -39,7 +39,22 @@ export interface RateLimiterStore { | ||
onLimited: (event: RequestEvent, reason: 'rate' | 'rejected') => Promise<void | boolean> | void | boolean; | ||
/** | ||
* @deprecated Add the IP/IPUA/cookie rates to the main object, no need for "rates". | ||
*/ | ||
rates: { | ||
/** | ||
* @deprecated Add the IP option to the main object, no need for "rates". | ||
*/ | ||
IP?: Rate; | ||
/** | ||
* @deprecated Add the IPUA option to the main object, no need for "rates". | ||
*/ | ||
IPUA?: Rate; | ||
/** | ||
* @deprecated Add cookie option to the main object, no need for "rates". | ||
*/ | ||
cookie?: CookieRateLimiterOptions; | ||
}; | ||
IP?: Rate; | ||
IPUA?: Rate; | ||
cookie?: CookieRateLimiterOptions; | ||
hashFunction: HashFunction; | ||
@@ -46,0 +61,0 @@ }>; |
@@ -93,26 +93,42 @@ import { nanoid } from 'nanoid'; | ||
static TTLTime(unit) { | ||
if (unit == 'ms') | ||
return 1; | ||
if (unit == 's') | ||
return 1000; | ||
if (unit == 'm') | ||
return 60 * 1000; | ||
if (unit == 'h') | ||
return 60 * 60 * 1000; | ||
if (unit == '15s') | ||
return 15 * 1000; | ||
if (unit == '30s') | ||
return 30 * 1000; | ||
if (unit == '15m') | ||
return 15 * 60 * 1000; | ||
if (unit == '30m') | ||
return 30 * 60 * 1000; | ||
if (unit == '2h') | ||
return 2 * 60 * 60 * 1000; | ||
if (unit == '6h') | ||
return 6 * 60 * 60 * 1000; | ||
if (unit == '12h') | ||
return 12 * 60 * 60 * 1000; | ||
if (unit == 'd') | ||
return 24 * 60 * 60 * 1000; | ||
switch (unit) { | ||
case 's': | ||
return 1000; | ||
case 'm': | ||
return 60000; | ||
case 'h': | ||
return 60 * 60000; | ||
case '2s': | ||
return 2000; | ||
case '5s': | ||
return 5000; | ||
case '10s': | ||
return 10000; | ||
case '15s': | ||
return 15000; | ||
case '30s': | ||
return 30000; | ||
case '45s': | ||
return 45000; | ||
case '15m': | ||
return 15 * 60000; | ||
case '30m': | ||
return 30 * 60000; | ||
case '100ms': | ||
return 100; | ||
case '250ms': | ||
return 250; | ||
case '500ms': | ||
return 500; | ||
case '2h': | ||
return 2 * 60 * 60000; | ||
case '6h': | ||
return 6 * 60 * 60000; | ||
case '12h': | ||
return 12 * 60 * 60000; | ||
case 'd': | ||
return 24 * 60 * 60000; | ||
case 'ms': | ||
return 1; | ||
} | ||
throw new Error('Invalid unit for TTLTime: ' + unit); | ||
@@ -140,3 +156,3 @@ } | ||
async _isLimited(event) { | ||
let indeterminate = false; | ||
let limited = false; | ||
for (const plugin of this.plugins) { | ||
@@ -156,7 +172,7 @@ const id = await plugin.hash(event); | ||
else if (id === null) { | ||
indeterminate = true; | ||
limited = true; | ||
continue; | ||
} | ||
else { | ||
indeterminate = false; | ||
limited = false; | ||
} | ||
@@ -178,3 +194,3 @@ if (!id) { | ||
return { | ||
limited: indeterminate, | ||
limited, | ||
hash: null, | ||
@@ -191,10 +207,13 @@ unit: this.plugins[this.plugins.length - 1].rate[1] | ||
} | ||
if (options.rates?.IP) | ||
this.plugins.push(new IPRateLimiter(options.rates.IP)); | ||
if (options.rates?.IPUA) | ||
this.plugins.push(new IPUserAgentRateLimiter(options.rates.IPUA)); | ||
if (options.rates?.cookie) { | ||
const IPRates = options.IP ?? options.rates?.IP; | ||
if (IPRates) | ||
this.plugins.push(new IPRateLimiter(IPRates)); | ||
const IPUARates = options.IPUA ?? options.rates?.IPUA; | ||
if (IPUARates) | ||
this.plugins.push(new IPUserAgentRateLimiter(IPUARates)); | ||
const cookieRates = options.cookie ?? options.rates?.cookie; | ||
if (cookieRates) { | ||
this.plugins.push((this.cookieLimiter = new CookieRateLimiter({ | ||
hashFunction: this.hashFunction, | ||
...options.rates.cookie | ||
...cookieRates | ||
}))); | ||
@@ -211,3 +230,7 @@ } | ||
const maxTTL = this.plugins.reduce((acc, plugin) => { | ||
const time = RateLimiter.TTLTime(plugin.rate[1]); | ||
const rate = plugin.rate[1]; | ||
if (rate == 'ms') { | ||
console.warn('RateLimiter: The "ms" unit is not reliable due to OS timing issues.'); | ||
} | ||
const time = RateLimiter.TTLTime(rate); | ||
return Math.max(time, acc); | ||
@@ -214,0 +237,0 @@ }, 0); |
{ | ||
"name": "sveltekit-rate-limiter", | ||
"version": "0.4.2", | ||
"version": "0.4.3", | ||
"author": "Andreas Söderlund <ciscoheat@gmail.com> (https://blog.encodeart.dev)", | ||
@@ -45,7 +45,7 @@ "description": "A modular rate limiter for SvelteKit. Use in password resets, account registration, etc.", | ||
"devDependencies": { | ||
"@sveltejs/adapter-auto": "^3.0.0", | ||
"@sveltejs/adapter-cloudflare": "^3.0.0", | ||
"@sveltejs/kit": "^2.0.0", | ||
"@sveltejs/package": "^2.2.3", | ||
"@sveltejs/vite-plugin-svelte": "^3.0.0", | ||
"@sveltejs/adapter-auto": "^3.1.0", | ||
"@sveltejs/adapter-cloudflare": "^3.0.2", | ||
"@sveltejs/kit": "^2.3.2", | ||
"@sveltejs/package": "^2.2.5", | ||
"@sveltejs/vite-plugin-svelte": "^3.0.1", | ||
"@typescript-eslint/eslint-plugin": "^5.62.0", | ||
@@ -60,9 +60,9 @@ "@typescript-eslint/parser": "^5.62.0", | ||
"prettier-plugin-svelte": "^2.10.1", | ||
"publint": "^0.2.6", | ||
"publint": "^0.2.7", | ||
"svelte": "^4.2.8", | ||
"svelte-check": "^3.6.2", | ||
"svelte-check": "^3.6.3", | ||
"tslib": "^2.6.2", | ||
"typescript": "^5.3.3", | ||
"vite": "^5.0.0", | ||
"vitest": "^1.0.0", | ||
"vite": "^5.0.11", | ||
"vitest": "^1.2.0", | ||
"vitest-mock-extended": "^1.3.1" | ||
@@ -69,0 +69,0 @@ }, |
# sveltekit-rate-limiter | ||
A modular rate limiter for password resets, account registration, etc. Use in your `page.server.ts` files, or `hooks.server.ts`. | ||
A modular rate limiter for password resets, account registration, etc. Use in your `+page.server.ts`, `+server.ts` or `src/hooks.server.ts`. | ||
@@ -25,12 +25,10 @@ Uses an in-memory cache ([@isaacs/ttlcache](https://www.npmjs.com/package/@isaacs/ttlcache)), but can be swapped for something else. Same for limiters, which are plugins. The [source file](https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/src/lib/server/index.ts#L24-L32) lists both interfaces. | ||
// A rate is defined as [number, unit] | ||
rates: { | ||
IP: [10, 'h'], // IP address limiter | ||
IPUA: [5, 'm'], // IP + User Agent limiter | ||
cookie: { | ||
// Cookie limiter | ||
name: 'limiterid', // Unique cookie name for this limiter | ||
secret: 'SECRETKEY-SERVER-ONLY', // Use $env/static/private | ||
rate: [2, 'm'], | ||
preflight: true // Require preflight call (see load function) | ||
} | ||
IP: [10, 'h'], // IP address limiter | ||
IPUA: [5, 'm'], // IP + User Agent limiter | ||
cookie: { | ||
// Cookie limiter | ||
name: 'limiterid', // Unique cookie name for this limiter | ||
secret: 'SECRETKEY-SERVER-ONLY', // Use $env/static/private | ||
rate: [2, 'm'], | ||
preflight: true // Require preflight call (see load function) | ||
} | ||
@@ -92,3 +90,7 @@ }); | ||
``` | ||
'ms' | 's' | '15s' | '30s' | 'm' | '15m' | '30m' | 'h' | '2h' | '6h' | '12h' | 'd' | ||
'100ms' | '250ms' | '500ms' | ||
's' | '2s' | '5s' | '10s' | '15s' | '30s' | '45s' | ||
'm' | '15m' | '30m' | ||
'h' | '2h' | '6h' | '12h' | ||
'd' | ||
``` | ||
@@ -105,6 +107,4 @@ | ||
const limiter = new RetryAfterRateLimiter({ | ||
rates: { | ||
IP: [10, 'h'], | ||
IPUA: [5, 'm'] | ||
} | ||
IP: [10, 'h'], | ||
IPUA: [5, 'm'] | ||
}); | ||
@@ -111,0 +111,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23941
426