64 lines
1.8 KiB
TypeScript
64 lines
1.8 KiB
TypeScript
import {
|
|
Injectable,
|
|
CanActivate,
|
|
ExecutionContext,
|
|
HttpException,
|
|
} from '@nestjs/common';
|
|
import { Request } from 'express';
|
|
import { IPLimitService } from 'src/modules/iplimit/iplimit.service';
|
|
import { AuditAction } from 'src/modules/objects/audit/audit.enum';
|
|
import { AuditService } from 'src/modules/objects/audit/audit.service';
|
|
|
|
@Injectable()
|
|
export class LoginAntispamGuard implements CanActivate {
|
|
constructor(
|
|
private iplimit: IPLimitService,
|
|
private audit: AuditService,
|
|
) {}
|
|
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
const request = context.switchToHttp().getRequest<Request>();
|
|
|
|
if (['GET', 'OPTIONS'].includes(request.method)) return true;
|
|
|
|
const known = await this.iplimit.getAddressLimit(request.ip);
|
|
if (known && known.attempts > 3) {
|
|
if (known.attempts > 5) {
|
|
let reported = false;
|
|
if (!known.reported) {
|
|
reported = true;
|
|
await this.audit.insertAudit(
|
|
AuditAction.THROTTLE,
|
|
`antispam-guard ${known.attempts} attempts`,
|
|
undefined,
|
|
request.ip,
|
|
request.header('user-agent'),
|
|
);
|
|
}
|
|
|
|
const limitMinutes = known.attempts > 10 ? 30 : 10; // Half-Hour
|
|
await this.iplimit.limitUntil(
|
|
request.ip,
|
|
limitMinutes * 60 * 1000,
|
|
reported,
|
|
);
|
|
|
|
await new Promise((resolve) =>
|
|
setTimeout(resolve, known.attempts * 1000),
|
|
);
|
|
|
|
throw new HttpException(
|
|
`Too Many Requests. Try again in ${limitMinutes} minutes.`,
|
|
429,
|
|
);
|
|
}
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
}
|
|
|
|
await this.iplimit.limitUntil(request.ip, 30 * 1000); // 30 seconds
|
|
|
|
return true;
|
|
}
|
|
}
|