|
|
|
@ -1,9 +1,12 @@
|
|
|
|
|
import { Catch, Logger, NotFoundException, Optional } from '@nestjs/common'; |
|
|
|
|
import { InjectSentry, SentryService } from '@ntegral/nestjs-sentry'; |
|
|
|
|
import { ThrottlerException } from '@nestjs/throttler'; |
|
|
|
|
import hash from 'object-hash'; |
|
|
|
|
import { NcErrorType } from 'nocodb-sdk'; |
|
|
|
|
import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common'; |
|
|
|
|
import type { Request, Response } from 'express'; |
|
|
|
|
import NocoCache from '~/cache/NocoCache'; |
|
|
|
|
import { CacheGetType } from '~/utils/globals'; |
|
|
|
|
import { |
|
|
|
|
AjvError, |
|
|
|
|
BadRequest, |
|
|
|
@ -70,12 +73,60 @@ export class GlobalExceptionFilter implements ExceptionFilter {
|
|
|
|
|
this.logError(exception, request); |
|
|
|
|
|
|
|
|
|
if (exception instanceof ThrottlerException) { |
|
|
|
|
const key = hash({ |
|
|
|
|
ip: request.ip, |
|
|
|
|
baseId: (request as any).ncBaseId, |
|
|
|
|
workspaceId: (request as any).ncWorkspaceId, |
|
|
|
|
path: request.path, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
const cacheKey = `throttler:${key}`; |
|
|
|
|
|
|
|
|
|
NocoCache.get(cacheKey, CacheGetType.TYPE_OBJECT) |
|
|
|
|
.then((data) => { |
|
|
|
|
if (!data) { |
|
|
|
|
this.logger.warn( |
|
|
|
|
`ThrottlerException: ${exception.message}, Path: ${ |
|
|
|
|
request.path |
|
|
|
|
}, Workspace ID: ${(request as any).ncWorkspaceId}, Base ID: ${ |
|
|
|
|
(request as any).ncBaseId |
|
|
|
|
}`,
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
NocoCache.setExpiring( |
|
|
|
|
cacheKey, |
|
|
|
|
{ value: true, count: 1, timestamp: Date.now() }, |
|
|
|
|
300, |
|
|
|
|
).catch((err) => { |
|
|
|
|
this.logger.error(err); |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
data.count += 1; |
|
|
|
|
|
|
|
|
|
const ttlInSeconds = Math.floor( |
|
|
|
|
(data.timestamp + 300000 - Date.now()) / 1000, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
NocoCache.setExpiring(cacheKey, data, ttlInSeconds).catch((err) => { |
|
|
|
|
this.logger.error(err); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// log every 50th request in last 5 minutes after first
|
|
|
|
|
if (data.count % 50 === 0) { |
|
|
|
|
this.logger.warn( |
|
|
|
|
`${exception.message}, Path : ${request.path}, Workspace ID : ${ |
|
|
|
|
(request as any).ncWorkspaceId |
|
|
|
|
}, Project ID : ${(request as any).ncBaseId}`,
|
|
|
|
|
`ThrottlerException: ${exception.message}, Path: ${ |
|
|
|
|
request.path |
|
|
|
|
}, Workspace ID: ${(request as any).ncWorkspaceId}, Base ID: ${ |
|
|
|
|
(request as any).ncBaseId |
|
|
|
|
}, Requests in last 5 minutes: ${data.count}`,
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
this.logger.error(err); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if sso error then redirect to ui with error in query parameter
|
|
|
|
|
if ( |
|
|
|
|