import { FastifyReply, FastifyRequest } from "fastify"; import { RequestRequirement } from "../RequestRequirement"; import { UserManagementServer } from "../../UserManagementServer"; import { MapList } from "../../../../browser/tools/MapList"; import { DateHelper } from "../../../../browser/date/DateHelper"; import { Duration } from "../../../../browser/date/Duration"; import { DateMath } from "../../../../browser/date/DateMath"; import { Message } from "../../../../browser/messages/Message"; export class NotTooManyRequests extends RequestRequirement { _trackedIPs = new Map(); _maxRequests = 50; _duration = Duration.fromMinutes( 2 ); _watchList = new Set(); _blockList = new Map() _blockDuration = Duration.fromHours( 4 ); async handle( request:FastifyRequest, reply:FastifyReply ):Promise { if ( this._blockList.has( request.ip ) ) { let blockTime = this._blockList.get( request.ip ); if ( ! DateMath.isExpired( new Date( blockTime + this._blockDuration ) ) ) { return Promise.resolve( [ Message.Error( "User blocked" ) ] ); } } let valid = this.updateIP( request.ip ); if ( ! valid ) { return Promise.resolve( [ Message.Error( "Too many requests" ) ] ); } return Promise.resolve( [] ); } async updateIP( ip:string ):Promise { MapList.add( this._trackedIPs, ip, DateHelper.nowMS() ); MapList.shiftToSize( this._trackedIPs, ip, this._maxRequests ); let lastRelevantDate = DateMath.addSeconds( DateHelper.now(), -this._duration ); let timeStamps = this._trackedIPs.get( ip ); let filteredStamps = timeStamps.filter( ts => DateMath.isAfter( lastRelevantDate, new Date( ts ) ) ); if ( filteredStamps.length < this._maxRequests ) { this._trackedIPs.set( ip, filteredStamps ); return Promise.resolve( true ); } this._trackedIPs.delete( ip ); if ( this._watchList.has( ip ) ) { this._blockList.set( ip, DateHelper.nowMS() ); this._watchList.delete( ip ); } else { this._watchList.add( ip ); } return Promise.resolve( false ); } }