38 lines
866 B
TypeScript
38 lines
866 B
TypeScript
|
|
const WINDOW_MS = 20 * 60 * 1000; // 20 minutes
|
||
|
|
const MAX_ATTEMPTS = 20;
|
||
|
|
|
||
|
|
interface Entry
|
||
|
|
{
|
||
|
|
count: number;
|
||
|
|
windowStart: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
const store = new Map<string, Entry>();
|
||
|
|
|
||
|
|
function delayMs( count: number ): number
|
||
|
|
{
|
||
|
|
if ( count <= 5 ) return 5_000;
|
||
|
|
if ( count <= 10 ) return 15_000;
|
||
|
|
return 30_000;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function checkForgotPasswordRate( ip: string ): { blocked: boolean; delay: number }
|
||
|
|
{
|
||
|
|
const now = Date.now();
|
||
|
|
let entry = store.get( ip );
|
||
|
|
|
||
|
|
if ( !entry || now - entry.windowStart > WINDOW_MS )
|
||
|
|
entry = { count: 0, windowStart: now };
|
||
|
|
|
||
|
|
entry.count++;
|
||
|
|
store.set( ip, entry );
|
||
|
|
|
||
|
|
if ( entry.count > MAX_ATTEMPTS ) return { blocked: true, delay: 0 };
|
||
|
|
return { blocked: false, delay: delayMs( entry.count ) };
|
||
|
|
}
|
||
|
|
|
||
|
|
export function sleep( ms: number ): Promise<void>
|
||
|
|
{
|
||
|
|
return new Promise( resolve => setTimeout( resolve, ms ) );
|
||
|
|
}
|