From 2ed493f5a23703e7026c39453d3d904fed954069 Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 15:52:33 +0530 Subject: [PATCH 1/9] Added logs for login and user read API --- src/auth/auth.controller.ts | 10 ++- src/auth/auth.service.ts | 144 +++++++++++++++++++++++++++++++- src/common/logger/LoggerUtil.ts | 109 +++++++++++++++++++----- src/user/user.controller.ts | 127 ++++++++++++++++++++++++++-- 4 files changed, 354 insertions(+), 36 deletions(-) diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index d199f9c3..d7ce8db5 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -30,7 +30,7 @@ import { AuthService } from "./auth.service"; import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; import { APIID } from "src/common/utils/api-id.config"; import { AllExceptionsFilter } from "src/common/filters/exception.filter"; -import { Response } from "express"; +import { Response, Request } from "express"; @ApiTags("Auth") @Controller("auth") @@ -43,8 +43,12 @@ export class AuthController { @UsePipes(ValidationPipe) @HttpCode(HttpStatus.OK) @ApiForbiddenResponse({ description: "Forbidden" }) - public async login(@Body() authDto: AuthDto, @Res() response: Response) { - return this.authService.login(authDto, response); + public async login( + @Body() authDto: AuthDto, + @Req() request: Request, + @Res() response: Response + ) { + return this.authService.login(authDto, request, response); } @UseFilters(new AllExceptionsFilter(APIID.USER_AUTH)) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index d3aa71a1..4bb734bb 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -11,6 +11,8 @@ import APIResponse from 'src/common/responses/response'; import { KeycloakService } from 'src/common/utils/keycloak.service'; import { APIID } from 'src/common/utils/api-id.config'; import { Response } from 'express'; +import { Request } from 'express'; +import { LoggerUtil } from 'src/common/logger/LoggerUtil'; type LoginResponse = { access_token: string; @@ -25,9 +27,22 @@ export class AuthService { private readonly keycloakService: KeycloakService ) {} - async login(authDto, response: Response) { + async login(authDto, request: Request, response: Response) { const apiId = APIID.LOGIN; const { username, password } = authDto; + + // Extract request information for logging + const clientIp = this.getClientIp(request); + const userAgent = request.headers['user-agent'] || 'Unknown'; + + // Log login attempt start + LoggerUtil.log( + `Login attempt initiated - Username: ${username}, IP: ${clientIp}, User-Agent: ${userAgent}`, + 'AuthService', + username, + 'info' + ); + try { // Fetch user details by username const userData = await this.useradapter @@ -40,6 +55,18 @@ export class AuthService { ? 'User details not found for user' : 'User is inactive, please verify your email'; + const failureReason = !userData + ? 'USER_NOT_FOUND' + : 'USER_INACTIVE'; + + // Log failed login attempt with reason and status code + LoggerUtil.error( + `Login failed - Username: ${username}, IP: ${clientIp}, StatusCode: ${HttpStatus.BAD_REQUEST}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: CLIENT_ERROR`, + errorMessage, + 'AuthService', + username + ); + return APIResponse.error( response, apiId, @@ -66,6 +93,14 @@ export class AuthService { token_type, }; + // Log successful login with status code + LoggerUtil.log( + `Login successful - Username: ${username}, IP: ${clientIp}, User-Agent: ${userAgent}, StatusCode: ${HttpStatus.OK}`, + 'AuthService', + username, + 'info' + ); + return APIResponse.success( response, apiId, @@ -75,9 +110,28 @@ export class AuthService { ); } catch (error) { if (error.response && error.response.status === 401) { + // Log invalid credentials with status code + LoggerUtil.error( + `Login failed - Username: ${username}, IP: ${clientIp}, StatusCode: ${HttpStatus.UNAUTHORIZED}, Reason: INVALID_CREDENTIALS, Message: Invalid username or password, IssueType: CLIENT_ERROR`, + 'Invalid username or password', + 'AuthService', + username + ); throw new NotFoundException('Invalid username or password'); } else { const errorMessage = error?.message || 'Something went wrong'; + const errorStack = error?.stack || 'No stack trace available'; + const httpStatus = error?.response?.status || HttpStatus.INTERNAL_SERVER_ERROR; + const issueType = httpStatus >= 500 ? 'SERVER_ERROR' : 'CLIENT_ERROR'; + + // Log error with status code and issue type + LoggerUtil.error( + `Login failed - Username: ${username}, IP: ${clientIp}, StatusCode: ${httpStatus}, Reason: INTERNAL_SERVER_ERROR, Message: ${errorMessage}, IssueType: ${issueType}`, + errorStack, + 'AuthService', + username + ); + return APIResponse.error( response, apiId, @@ -89,15 +143,70 @@ export class AuthService { } } + /** + * Extract client IP address from request + * Handles proxy headers (X-Forwarded-For, X-Real-IP) + */ + private getClientIp(request: Request): string { + const forwarded = request.headers['x-forwarded-for']; + if (forwarded) { + // X-Forwarded-For can contain multiple IPs, take the first one + const ips = Array.isArray(forwarded) ? forwarded[0] : forwarded; + return ips.split(',')[0].trim(); + } + + const realIp = request.headers['x-real-ip']; + if (realIp) { + return Array.isArray(realIp) ? realIp[0] : realIp; + } + + // Fallback to request IP or socket remote address + return request.ip || request.socket?.remoteAddress || 'Unknown'; + } + public async getUserByAuth(request: any, tenantId, response: Response) { const apiId = APIID.USER_AUTH; + + // Extract request information for logging + const clientIp = this.getClientIp(request); + const userAgent = request.headers['user-agent'] || 'Unknown'; + let username = 'Unknown'; + let userId = 'Unknown'; + try { + // Log API call attempt + LoggerUtil.log( + `GetUserByAuth attempt - IP: ${clientIp}, User-Agent: ${userAgent}, TenantId: ${tenantId || 'Not provided'}`, + 'AuthService', + username, + 'info' + ); + + // Decode JWT token to get username const decoded: any = jwt_decode(request.headers.authorization); - const username = decoded.preferred_username; + username = decoded.preferred_username || 'Unknown'; + userId = decoded.sub || 'Unknown'; + + // Log with username after decoding + LoggerUtil.log( + `GetUserByAuth processing - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, TenantId: ${tenantId || 'Not provided'}`, + 'AuthService', + username, + 'info' + ); + const data = await this.useradapter .buildUserAdapter() .findUserDetails(null, username, tenantId); + // Log successful response + LoggerUtil.log( + `GetUserByAuth successful - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, StatusCode: ${HttpStatus.OK}`, + 'AuthService', + username, + 'info' + ); + return APIResponse.success( response, apiId, @@ -107,12 +216,41 @@ export class AuthService { ); } catch (e) { const errorMessage = e?.message || 'Something went wrong'; + const errorStack = e?.stack || 'No stack trace available'; + + // Determine error type and status code + let httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + let failureReason = 'INTERNAL_SERVER_ERROR'; + let issueType = 'SERVER_ERROR'; + + if (e.name === 'JsonWebTokenError' || e.message?.includes('token') || e.message?.includes('jwt')) { + httpStatus = HttpStatus.UNAUTHORIZED; + failureReason = 'INVALID_TOKEN'; + issueType = 'CLIENT_ERROR'; + } else if (e.message?.includes('not found') || e.message?.includes('does not exist')) { + httpStatus = HttpStatus.NOT_FOUND; + failureReason = 'USER_NOT_FOUND'; + issueType = 'CLIENT_ERROR'; + } else if (e.message?.includes('unauthorized') || e.message?.includes('forbidden')) { + httpStatus = HttpStatus.FORBIDDEN; + failureReason = 'UNAUTHORIZED'; + issueType = 'CLIENT_ERROR'; + } + + // Log failed attempt with comprehensive details + LoggerUtil.error( + `GetUserByAuth failed - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, StatusCode: ${httpStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId || 'Not provided'}`, + errorStack, + 'AuthService', + username + ); + return APIResponse.error( response, apiId, 'Internal Server Error', `Error : ${errorMessage}`, - HttpStatus.INTERNAL_SERVER_ERROR + httpStatus ); } } diff --git a/src/common/logger/LoggerUtil.ts b/src/common/logger/LoggerUtil.ts index 1d1bbbd1..98146d14 100644 --- a/src/common/logger/LoggerUtil.ts +++ b/src/common/logger/LoggerUtil.ts @@ -22,57 +22,120 @@ export class LoggerUtil { level: 'info', format: winston.format.combine(winston.format.timestamp(), customFormat), transports: [ - new winston.transports.Console(), - new winston.transports.File({ filename: 'error.log', level: 'error' }), - new winston.transports.File({ filename: 'combined.log' }), + new winston.transports.Console({ + // Console logging is fast and non-blocking + handleExceptions: false, + handleRejections: false, + }), + new winston.transports.File({ + filename: 'error.log', + level: 'error', + // Optimize file writes for performance + maxsize: 5242880, // 5MB + maxFiles: 5, + tailable: true, + }), + new winston.transports.File({ + filename: 'combined.log', + // Optimize file writes for performance + maxsize: 5242880, // 5MB + maxFiles: 5, + tailable: true, + }), ], + // Exit on error to prevent logging failures from crashing the app + exitOnError: false, }); } return this.logger; } + + /** + * Non-blocking log method - uses process.nextTick to offload logging + * This ensures API responses are not delayed by logging operations + */ static log( message: string, context?: string, user?: string, level: string = 'info', ) { - this.getLogger().log({ - level: level, - message: message, - context: context, - user: user, - timestamp: new Date().toISOString(), + // Use process.nextTick to make logging non-blocking + // This ensures the API response is sent before logging completes + process.nextTick(() => { + try { + this.getLogger().log({ + level: level, + message: message, + context: context, + user: user, + timestamp: new Date().toISOString(), + }); + } catch (err) { + // Silently fail - don't let logging errors affect API responses + // Only log to console as last resort + console.error('Logger error:', err); + } }); } + /** + * Non-blocking error log method + */ static error( message: string, error?: string, context?: string, user?: string, ) { - this.getLogger().error({ - message: message, - error: error, - context: context, - user: user, - timestamp: new Date().toISOString(), + // Use process.nextTick to make logging non-blocking + process.nextTick(() => { + try { + this.getLogger().error({ + message: message, + error: error, + context: context, + user: user, + timestamp: new Date().toISOString(), + }); + } catch (err) { + // Silently fail - don't let logging errors affect API responses + console.error('Logger error:', err); + } }); } + /** + * Non-blocking warn log method + */ static warn(message: string, context?: string) { - this.getLogger().warn({ - message: message, - context: context, - timestamp: new Date().toISOString(), + process.nextTick(() => { + try { + this.getLogger().warn({ + message: message, + context: context, + timestamp: new Date().toISOString(), + }); + } catch (err) { + console.error('Logger error:', err); + } }); } + /** + * Non-blocking debug log method + */ static debug(message: string, context?: string) { - this.getLogger().debug({ - message: message, - context: context, - timestamp: new Date().toISOString(), + process.nextTick(() => { + try { + this.getLogger().debug({ + message: message, + context: context, + timestamp: new Date().toISOString(), + }); + } catch (err) { + console.error('Logger error:', err); + } }); } } \ No newline at end of file diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 1fbfb656..0b73e369 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -18,6 +18,7 @@ import { UseFilters, BadRequestException, UnauthorizedException, + HttpStatus, } from '@nestjs/common'; import { @@ -95,16 +96,47 @@ export class UserController { @Param('userId', ParseUUIDPipe) userId: string, @Query('fieldvalue') fieldvalue: string | null = null ) { + // Extract request information for logging + const clientIp = this.getClientIp(request); + const userAgent = request.headers['user-agent'] || 'Unknown'; + let requesterUsername = 'Unknown'; + let requesterUserId = 'Unknown'; + + try { + // Extract requester info from JWT token if available + if (request.headers.authorization) { + const jwt_decode = require('jwt-decode'); + const decoded: any = jwt_decode(request.headers.authorization); + requesterUsername = decoded.preferred_username || 'Unknown'; + requesterUserId = decoded.sub || 'Unknown'; + } + } catch (e) { + // If token decode fails, continue with Unknown values + } + const tenantId = headers['tenantid']; + + // Log API call attempt + LoggerUtil.log( + `GetUser attempt - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, RequesterUserId: ${requesterUserId}, IP: ${clientIp}, TenantId: ${tenantId || 'Not provided'}, FieldValue: ${fieldvalue || 'false'}`, + 'UserController', + requesterUsername, + 'info' + ); + if (!tenantId) { - LoggerUtil.warn( - `${API_RESPONSES.BAD_REQUEST}`, - `Error: Missing tenantId in request headers for user ${userId}` + // Log missing tenantId error + LoggerUtil.error( + `GetUser failed - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: 400, Reason: MISSING_TENANT_ID, Message: Missing tenantId in request headers, IssueType: CLIENT_ERROR`, + 'Missing tenantId in request headers', + 'UserController', + requesterUsername ); return response .status(400) .json({ statusCode: 400, error: 'Please provide a tenantId.' }); } + const fieldValueBoolean = fieldvalue === 'true'; // Context and ContextType can be taken from .env later const userData: UserData = { @@ -113,11 +145,92 @@ export class UserController { userId: userId, fieldValue: fieldValueBoolean, }; - const result = await this.userAdapter - .buildUserAdapter() - .getUsersDetailsById(userData, response); - return response.status(result.statusCode).json(result); + try { + const result = await this.userAdapter + .buildUserAdapter() + .getUsersDetailsById(userData, response); + + const statusCode = result.statusCode || 200; + + // Determine if successful or failed based on status code + if (statusCode >= 200 && statusCode < 300) { + // Log successful response + LoggerUtil.log( + `GetUser successful - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: ${statusCode}, TenantId: ${tenantId}`, + 'UserController', + requesterUsername, + 'info' + ); + } else { + // Log failed response with reason + let failureReason = 'UNKNOWN_ERROR'; + let issueType = 'SERVER_ERROR'; + + if (statusCode === 400) { + failureReason = 'BAD_REQUEST'; + issueType = 'CLIENT_ERROR'; + } else if (statusCode === 404) { + failureReason = 'USER_NOT_FOUND'; + issueType = 'CLIENT_ERROR'; + } else if (statusCode === 401 || statusCode === 403) { + failureReason = 'UNAUTHORIZED'; + issueType = 'CLIENT_ERROR'; + } else if (statusCode >= 500) { + failureReason = 'INTERNAL_SERVER_ERROR'; + issueType = 'SERVER_ERROR'; + } + + LoggerUtil.error( + `GetUser failed - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: ${statusCode}, Reason: ${failureReason}, Message: ${result.message || result.error || 'Unknown error'}, IssueType: ${issueType}, TenantId: ${tenantId}`, + result.error || result.message || 'Unknown error', + 'UserController', + requesterUsername + ); + } + + return response.status(statusCode).json(result); + } catch (error) { + const errorMessage = error?.message || 'Something went wrong'; + const errorStack = error?.stack || 'No stack trace available'; + const httpStatus = error?.status || HttpStatus.INTERNAL_SERVER_ERROR; + const issueType = httpStatus >= 500 ? 'SERVER_ERROR' : 'CLIENT_ERROR'; + + // Log exception with comprehensive details + LoggerUtil.error( + `GetUser exception - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: ${httpStatus}, Reason: EXCEPTION, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId}`, + errorStack, + 'UserController', + requesterUsername + ); + + return response.status(httpStatus).json({ + statusCode: httpStatus, + error: errorMessage, + message: 'An error occurred while fetching user details', + }); + } + } + + /** + * Extract client IP address from request + * Handles proxy headers (X-Forwarded-For, X-Real-IP) + */ + private getClientIp(request: Request): string { + const forwarded = request.headers['x-forwarded-for']; + if (forwarded) { + // X-Forwarded-For can contain multiple IPs, take the first one + const ips = Array.isArray(forwarded) ? forwarded[0] : forwarded; + return ips.split(',')[0].trim(); + } + + const realIp = request.headers['x-real-ip']; + if (realIp) { + return Array.isArray(realIp) ? realIp[0] : realIp; + } + + // Fallback to request IP or socket remote address + return request.ip || request.socket?.remoteAddress || 'Unknown'; } @UseFilters(new AllExceptionsFilter(APIID.USER_CREATE)) From 0812126ea2ce5911a00db852ad0f115bb8045914 Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 17:25:05 +0530 Subject: [PATCH 2/9] Added logs for login and user read API --- src/auth/auth.service.ts | 17 +++++++++-------- src/common/logger/LoggerUtil.ts | 21 ++++++++++++--------- src/user/user.controller.ts | 8 ++++++-- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 4bb734bb..f6d3248f 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -218,39 +218,40 @@ export class AuthService { const errorMessage = e?.message || 'Something went wrong'; const errorStack = e?.stack || 'No stack trace available'; - // Determine error type and status code - let httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + // Determine error type for logging purposes (but keep API response consistent) + let detectedStatus = HttpStatus.INTERNAL_SERVER_ERROR; let failureReason = 'INTERNAL_SERVER_ERROR'; let issueType = 'SERVER_ERROR'; if (e.name === 'JsonWebTokenError' || e.message?.includes('token') || e.message?.includes('jwt')) { - httpStatus = HttpStatus.UNAUTHORIZED; + detectedStatus = HttpStatus.UNAUTHORIZED; failureReason = 'INVALID_TOKEN'; issueType = 'CLIENT_ERROR'; } else if (e.message?.includes('not found') || e.message?.includes('does not exist')) { - httpStatus = HttpStatus.NOT_FOUND; + detectedStatus = HttpStatus.NOT_FOUND; failureReason = 'USER_NOT_FOUND'; issueType = 'CLIENT_ERROR'; } else if (e.message?.includes('unauthorized') || e.message?.includes('forbidden')) { - httpStatus = HttpStatus.FORBIDDEN; + detectedStatus = HttpStatus.FORBIDDEN; failureReason = 'UNAUTHORIZED'; issueType = 'CLIENT_ERROR'; } - // Log failed attempt with comprehensive details + // Log failed attempt with comprehensive details (including detected status for monitoring) LoggerUtil.error( - `GetUserByAuth failed - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, StatusCode: ${httpStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId || 'Not provided'}`, + `GetUserByAuth failed - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, DetectedStatusCode: ${detectedStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId || 'Not provided'}`, errorStack, 'AuthService', username ); + // Keep original API response behavior - always return INTERNAL_SERVER_ERROR return APIResponse.error( response, apiId, 'Internal Server Error', `Error : ${errorMessage}`, - httpStatus + HttpStatus.INTERNAL_SERVER_ERROR ); } } diff --git a/src/common/logger/LoggerUtil.ts b/src/common/logger/LoggerUtil.ts index 98146d14..17b57d40 100644 --- a/src/common/logger/LoggerUtil.ts +++ b/src/common/logger/LoggerUtil.ts @@ -30,20 +30,23 @@ export class LoggerUtil { new winston.transports.File({ filename: 'error.log', level: 'error', - // Optimize file writes for performance - maxsize: 5242880, // 5MB - maxFiles: 5, - tailable: true, + // Log rotation configuration for performance and disk space management + // When log file reaches 5MB, it will be rotated (renamed) and a new file created + maxsize: 5242880, // 5MB - maximum size before rotation + maxFiles: 5, // Keep maximum 5 rotated log files (error.log, error.log.1, error.log.2, etc.) + tailable: true, // Oldest logs are deleted when maxFiles is reached }), new winston.transports.File({ filename: 'combined.log', - // Optimize file writes for performance - maxsize: 5242880, // 5MB - maxFiles: 5, - tailable: true, + // Log rotation configuration for performance and disk space management + // When log file reaches 5MB, it will be rotated (renamed) and a new file created + maxsize: 5242880, // 5MB - maximum size before rotation + maxFiles: 5, // Keep maximum 5 rotated log files (combined.log, combined.log.1, combined.log.2, etc.) + tailable: true, // Oldest logs are deleted when maxFiles is reached }), ], - // Exit on error to prevent logging failures from crashing the app + // Prevent Winston from exiting the process when logging errors occur + // This ensures logging failures don't crash the application exitOnError: false, }); } diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 0b73e369..7f786f75 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -56,6 +56,7 @@ import { OtpSendDTO } from './dto/otpSend.dto'; import { OtpVerifyDTO } from './dto/otpVerify.dto'; import { UserCreateSsoDto } from './dto/user-create-sso.dto'; import { RecaptchaService } from './recaptcha.service'; +import jwt_decode from 'jwt-decode'; export interface UserData { context: string; @@ -105,13 +106,16 @@ export class UserController { try { // Extract requester info from JWT token if available if (request.headers.authorization) { - const jwt_decode = require('jwt-decode'); const decoded: any = jwt_decode(request.headers.authorization); requesterUsername = decoded.preferred_username || 'Unknown'; requesterUserId = decoded.sub || 'Unknown'; } } catch (e) { - // If token decode fails, continue with Unknown values + // If token decode fails, log the error and continue with Unknown values + LoggerUtil.warn( + `Failed to decode JWT token for getUser request - IP: ${clientIp}, Error: ${e?.message || 'Unknown error'}`, + 'UserController' + ); } const tenantId = headers['tenantid']; From 59b6e6f0236e3bac3c04cb1fa20745e55e60dafb Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 17:58:52 +0530 Subject: [PATCH 3/9] Added logs for login and user read API --- src/auth/auth.service.ts | 56 ++++++++++++++++++------------------- src/user/user.controller.ts | 33 +++++++++++----------- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index f6d3248f..52e37da0 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -32,14 +32,13 @@ export class AuthService { const { username, password } = authDto; // Extract request information for logging - const clientIp = this.getClientIp(request); const userAgent = request.headers['user-agent'] || 'Unknown'; - // Log login attempt start + // Log login attempt start (username and IP excluded for legal compliance) LoggerUtil.log( - `Login attempt initiated - Username: ${username}, IP: ${clientIp}, User-Agent: ${userAgent}`, + `Login attempt initiated - User-Agent: ${userAgent}`, 'AuthService', - username, + undefined, // Username excluded for legal compliance 'info' ); @@ -59,12 +58,12 @@ export class AuthService { ? 'USER_NOT_FOUND' : 'USER_INACTIVE'; - // Log failed login attempt with reason and status code + // Log failed login attempt with reason and status code (username and IP excluded for legal compliance) LoggerUtil.error( - `Login failed - Username: ${username}, IP: ${clientIp}, StatusCode: ${HttpStatus.BAD_REQUEST}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: CLIENT_ERROR`, + `Login failed - StatusCode: ${HttpStatus.BAD_REQUEST}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: CLIENT_ERROR`, errorMessage, 'AuthService', - username + undefined // Username excluded for legal compliance ); return APIResponse.error( @@ -93,11 +92,11 @@ export class AuthService { token_type, }; - // Log successful login with status code + // Log successful login with status code (username and IP excluded for legal compliance) LoggerUtil.log( - `Login successful - Username: ${username}, IP: ${clientIp}, User-Agent: ${userAgent}, StatusCode: ${HttpStatus.OK}`, + `Login successful - User-Agent: ${userAgent}, StatusCode: ${HttpStatus.OK}`, 'AuthService', - username, + undefined, // Username excluded for legal compliance 'info' ); @@ -110,12 +109,12 @@ export class AuthService { ); } catch (error) { if (error.response && error.response.status === 401) { - // Log invalid credentials with status code + // Log invalid credentials with status code (username and IP excluded for legal compliance) LoggerUtil.error( - `Login failed - Username: ${username}, IP: ${clientIp}, StatusCode: ${HttpStatus.UNAUTHORIZED}, Reason: INVALID_CREDENTIALS, Message: Invalid username or password, IssueType: CLIENT_ERROR`, + `Login failed - StatusCode: ${HttpStatus.UNAUTHORIZED}, Reason: INVALID_CREDENTIALS, Message: Invalid username or password, IssueType: CLIENT_ERROR`, 'Invalid username or password', 'AuthService', - username + undefined // Username excluded for legal compliance ); throw new NotFoundException('Invalid username or password'); } else { @@ -124,12 +123,12 @@ export class AuthService { const httpStatus = error?.response?.status || HttpStatus.INTERNAL_SERVER_ERROR; const issueType = httpStatus >= 500 ? 'SERVER_ERROR' : 'CLIENT_ERROR'; - // Log error with status code and issue type + // Log error with status code and issue type (username and IP excluded for legal compliance) LoggerUtil.error( - `Login failed - Username: ${username}, IP: ${clientIp}, StatusCode: ${httpStatus}, Reason: INTERNAL_SERVER_ERROR, Message: ${errorMessage}, IssueType: ${issueType}`, + `Login failed - StatusCode: ${httpStatus}, Reason: INTERNAL_SERVER_ERROR, Message: ${errorMessage}, IssueType: ${issueType}`, errorStack, 'AuthService', - username + undefined // Username excluded for legal compliance ); return APIResponse.error( @@ -168,17 +167,16 @@ export class AuthService { const apiId = APIID.USER_AUTH; // Extract request information for logging - const clientIp = this.getClientIp(request); const userAgent = request.headers['user-agent'] || 'Unknown'; let username = 'Unknown'; let userId = 'Unknown'; try { - // Log API call attempt + // Log API call attempt (username and IP excluded for legal compliance) LoggerUtil.log( - `GetUserByAuth attempt - IP: ${clientIp}, User-Agent: ${userAgent}, TenantId: ${tenantId || 'Not provided'}`, + `GetUserByAuth attempt - User-Agent: ${userAgent}, TenantId: ${tenantId || 'Not provided'}`, 'AuthService', - username, + undefined, // Username excluded for legal compliance 'info' ); @@ -187,11 +185,11 @@ export class AuthService { username = decoded.preferred_username || 'Unknown'; userId = decoded.sub || 'Unknown'; - // Log with username after decoding + // Log with username after decoding (username, userId, and IP excluded for legal compliance) LoggerUtil.log( - `GetUserByAuth processing - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, TenantId: ${tenantId || 'Not provided'}`, + `GetUserByAuth processing - TenantId: ${tenantId || 'Not provided'}`, 'AuthService', - username, + undefined, // Username excluded for legal compliance 'info' ); @@ -199,11 +197,11 @@ export class AuthService { .buildUserAdapter() .findUserDetails(null, username, tenantId); - // Log successful response + // Log successful response (username, userId, and IP excluded for legal compliance) LoggerUtil.log( - `GetUserByAuth successful - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, StatusCode: ${HttpStatus.OK}`, + `GetUserByAuth successful - StatusCode: ${HttpStatus.OK}`, 'AuthService', - username, + undefined, // Username excluded for legal compliance 'info' ); @@ -237,12 +235,12 @@ export class AuthService { issueType = 'CLIENT_ERROR'; } - // Log failed attempt with comprehensive details (including detected status for monitoring) + // Log failed attempt with comprehensive details (username, userId, and IP excluded for legal compliance) LoggerUtil.error( - `GetUserByAuth failed - Username: ${username}, UserId: ${userId}, IP: ${clientIp}, DetectedStatusCode: ${detectedStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId || 'Not provided'}`, + `GetUserByAuth failed - DetectedStatusCode: ${detectedStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId || 'Not provided'}`, errorStack, 'AuthService', - username + undefined // Username excluded for legal compliance ); // Keep original API response behavior - always return INTERNAL_SERVER_ERROR diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 7f786f75..02ebe068 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -111,30 +111,30 @@ export class UserController { requesterUserId = decoded.sub || 'Unknown'; } } catch (e) { - // If token decode fails, log the error and continue with Unknown values + // If token decode fails, log the error and continue with Unknown values (IP excluded for legal compliance) LoggerUtil.warn( - `Failed to decode JWT token for getUser request - IP: ${clientIp}, Error: ${e?.message || 'Unknown error'}`, + `Failed to decode JWT token for getUser request - Error: ${e?.message || 'Unknown error'}`, 'UserController' ); } const tenantId = headers['tenantid']; - // Log API call attempt + // Log API call attempt (username, userId, and IP excluded for legal compliance) LoggerUtil.log( - `GetUser attempt - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, RequesterUserId: ${requesterUserId}, IP: ${clientIp}, TenantId: ${tenantId || 'Not provided'}, FieldValue: ${fieldvalue || 'false'}`, + `GetUser attempt - TenantId: ${tenantId || 'Not provided'}, FieldValue: ${fieldvalue || 'false'}`, 'UserController', - requesterUsername, + undefined, // Username excluded for legal compliance 'info' ); if (!tenantId) { - // Log missing tenantId error + // Log missing tenantId error (username, userId, and IP excluded for legal compliance) LoggerUtil.error( - `GetUser failed - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: 400, Reason: MISSING_TENANT_ID, Message: Missing tenantId in request headers, IssueType: CLIENT_ERROR`, + `GetUser failed - StatusCode: 400, Reason: MISSING_TENANT_ID, Message: Missing tenantId in request headers, IssueType: CLIENT_ERROR`, 'Missing tenantId in request headers', 'UserController', - requesterUsername + undefined // Username excluded for legal compliance ); return response .status(400) @@ -159,11 +159,11 @@ export class UserController { // Determine if successful or failed based on status code if (statusCode >= 200 && statusCode < 300) { - // Log successful response + // Log successful response (username, userId, and IP excluded for legal compliance) LoggerUtil.log( - `GetUser successful - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: ${statusCode}, TenantId: ${tenantId}`, + `GetUser successful - StatusCode: ${statusCode}, TenantId: ${tenantId}`, 'UserController', - requesterUsername, + undefined, // Username excluded for legal compliance 'info' ); } else { @@ -185,11 +185,12 @@ export class UserController { issueType = 'SERVER_ERROR'; } + // Log failed response (username, userId, and IP excluded for legal compliance) LoggerUtil.error( - `GetUser failed - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: ${statusCode}, Reason: ${failureReason}, Message: ${result.message || result.error || 'Unknown error'}, IssueType: ${issueType}, TenantId: ${tenantId}`, + `GetUser failed - StatusCode: ${statusCode}, Reason: ${failureReason}, Message: ${result.message || result.error || 'Unknown error'}, IssueType: ${issueType}, TenantId: ${tenantId}`, result.error || result.message || 'Unknown error', 'UserController', - requesterUsername + undefined // Username excluded for legal compliance ); } @@ -200,12 +201,12 @@ export class UserController { const httpStatus = error?.status || HttpStatus.INTERNAL_SERVER_ERROR; const issueType = httpStatus >= 500 ? 'SERVER_ERROR' : 'CLIENT_ERROR'; - // Log exception with comprehensive details + // Log exception with comprehensive details (username, userId, and IP excluded for legal compliance) LoggerUtil.error( - `GetUser exception - RequestedUserId: ${userId}, RequesterUsername: ${requesterUsername}, IP: ${clientIp}, StatusCode: ${httpStatus}, Reason: EXCEPTION, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId}`, + `GetUser exception - StatusCode: ${httpStatus}, Reason: EXCEPTION, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId}`, errorStack, 'UserController', - requesterUsername + undefined // Username excluded for legal compliance ); return response.status(httpStatus).json({ From 48471d36f237841f107d88d386da63dc79f5afdf Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 18:06:13 +0530 Subject: [PATCH 4/9] Added logs for login and user read API --- src/auth/auth.service.ts | 52 ++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 52e37da0..9d1bfc61 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -13,6 +13,7 @@ import { APIID } from 'src/common/utils/api-id.config'; import { Response } from 'express'; import { Request } from 'express'; import { LoggerUtil } from 'src/common/logger/LoggerUtil'; +import { AuthDto } from './dto/auth-dto'; type LoginResponse = { access_token: string; @@ -27,7 +28,7 @@ export class AuthService { private readonly keycloakService: KeycloakService ) {} - async login(authDto, request: Request, response: Response) { + async login(authDto: AuthDto, request: Request, response: Response) { const apiId = APIID.LOGIN; const { username, password } = authDto; @@ -123,9 +124,35 @@ export class AuthService { const httpStatus = error?.response?.status || HttpStatus.INTERNAL_SERVER_ERROR; const issueType = httpStatus >= 500 ? 'SERVER_ERROR' : 'CLIENT_ERROR'; + // Determine failure reason based on httpStatus + let failureReason = 'INTERNAL_SERVER_ERROR'; + if (httpStatus >= 400 && httpStatus < 500) { + if (httpStatus === 400) { + failureReason = 'BAD_REQUEST'; + } else if (httpStatus === 403) { + failureReason = 'FORBIDDEN'; + } else if (httpStatus === 404) { + failureReason = 'NOT_FOUND'; + } else if (httpStatus === 429) { + failureReason = 'RATE_LIMIT_EXCEEDED'; + } else { + failureReason = 'CLIENT_ERROR'; + } + } else if (httpStatus >= 500) { + if (httpStatus === 502) { + failureReason = 'BAD_GATEWAY'; + } else if (httpStatus === 503) { + failureReason = 'SERVICE_UNAVAILABLE'; + } else if (httpStatus === 504) { + failureReason = 'GATEWAY_TIMEOUT'; + } else { + failureReason = 'INTERNAL_SERVER_ERROR'; + } + } + // Log error with status code and issue type (username and IP excluded for legal compliance) LoggerUtil.error( - `Login failed - StatusCode: ${httpStatus}, Reason: INTERNAL_SERVER_ERROR, Message: ${errorMessage}, IssueType: ${issueType}`, + `Login failed - StatusCode: ${httpStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}`, errorStack, 'AuthService', undefined // Username excluded for legal compliance @@ -142,27 +169,6 @@ export class AuthService { } } - /** - * Extract client IP address from request - * Handles proxy headers (X-Forwarded-For, X-Real-IP) - */ - private getClientIp(request: Request): string { - const forwarded = request.headers['x-forwarded-for']; - if (forwarded) { - // X-Forwarded-For can contain multiple IPs, take the first one - const ips = Array.isArray(forwarded) ? forwarded[0] : forwarded; - return ips.split(',')[0].trim(); - } - - const realIp = request.headers['x-real-ip']; - if (realIp) { - return Array.isArray(realIp) ? realIp[0] : realIp; - } - - // Fallback to request IP or socket remote address - return request.ip || request.socket?.remoteAddress || 'Unknown'; - } - public async getUserByAuth(request: any, tenantId, response: Response) { const apiId = APIID.USER_AUTH; From 4be89e95815ebb69dbddd902918e1a6f29012754 Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 18:12:38 +0530 Subject: [PATCH 5/9] Added logs for login and user read API --- src/auth/auth.service.ts | 55 ++++++++++++++++++++++--------------- src/user/user.controller.ts | 6 +--- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 9d1bfc61..34c1f0da 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -10,8 +10,7 @@ import jwt_decode from 'jwt-decode'; import APIResponse from 'src/common/responses/response'; import { KeycloakService } from 'src/common/utils/keycloak.service'; import { APIID } from 'src/common/utils/api-id.config'; -import { Response } from 'express'; -import { Request } from 'express'; +import { Response, Request } from 'express'; import { LoggerUtil } from 'src/common/logger/LoggerUtil'; import { AuthDto } from './dto/auth-dto'; @@ -31,7 +30,7 @@ export class AuthService { async login(authDto: AuthDto, request: Request, response: Response) { const apiId = APIID.LOGIN; const { username, password } = authDto; - + // Extract request information for logging const userAgent = request.headers['user-agent'] || 'Unknown'; @@ -55,16 +54,14 @@ export class AuthService { ? 'User details not found for user' : 'User is inactive, please verify your email'; - const failureReason = !userData - ? 'USER_NOT_FOUND' - : 'USER_INACTIVE'; + const failureReason = !userData ? 'USER_NOT_FOUND' : 'USER_INACTIVE'; // Log failed login attempt with reason and status code (username and IP excluded for legal compliance) LoggerUtil.error( `Login failed - StatusCode: ${HttpStatus.BAD_REQUEST}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: CLIENT_ERROR`, errorMessage, 'AuthService', - undefined // Username excluded for legal compliance + undefined ); return APIResponse.error( @@ -115,15 +112,16 @@ export class AuthService { `Login failed - StatusCode: ${HttpStatus.UNAUTHORIZED}, Reason: INVALID_CREDENTIALS, Message: Invalid username or password, IssueType: CLIENT_ERROR`, 'Invalid username or password', 'AuthService', - undefined // Username excluded for legal compliance + undefined ); throw new NotFoundException('Invalid username or password'); } else { const errorMessage = error?.message || 'Something went wrong'; const errorStack = error?.stack || 'No stack trace available'; - const httpStatus = error?.response?.status || HttpStatus.INTERNAL_SERVER_ERROR; + const httpStatus = + error?.response?.status || HttpStatus.INTERNAL_SERVER_ERROR; const issueType = httpStatus >= 500 ? 'SERVER_ERROR' : 'CLIENT_ERROR'; - + // Determine failure reason based on httpStatus let failureReason = 'INTERNAL_SERVER_ERROR'; if (httpStatus >= 400 && httpStatus < 500) { @@ -145,17 +143,16 @@ export class AuthService { failureReason = 'SERVICE_UNAVAILABLE'; } else if (httpStatus === 504) { failureReason = 'GATEWAY_TIMEOUT'; - } else { - failureReason = 'INTERNAL_SERVER_ERROR'; } + // failureReason already defaults to 'INTERNAL_SERVER_ERROR' for other 5xx errors } - + // Log error with status code and issue type (username and IP excluded for legal compliance) LoggerUtil.error( `Login failed - StatusCode: ${httpStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}`, errorStack, 'AuthService', - undefined // Username excluded for legal compliance + undefined ); return APIResponse.error( @@ -171,16 +168,18 @@ export class AuthService { public async getUserByAuth(request: any, tenantId, response: Response) { const apiId = APIID.USER_AUTH; - + // Extract request information for logging const userAgent = request.headers['user-agent'] || 'Unknown'; let username = 'Unknown'; let userId = 'Unknown'; - + try { // Log API call attempt (username and IP excluded for legal compliance) LoggerUtil.log( - `GetUserByAuth attempt - User-Agent: ${userAgent}, TenantId: ${tenantId || 'Not provided'}`, + `GetUserByAuth attempt - User-Agent: ${userAgent}, TenantId: ${ + tenantId || 'Not provided' + }`, 'AuthService', undefined, // Username excluded for legal compliance 'info' @@ -221,21 +220,31 @@ export class AuthService { } catch (e) { const errorMessage = e?.message || 'Something went wrong'; const errorStack = e?.stack || 'No stack trace available'; - + // Determine error type for logging purposes (but keep API response consistent) let detectedStatus = HttpStatus.INTERNAL_SERVER_ERROR; let failureReason = 'INTERNAL_SERVER_ERROR'; let issueType = 'SERVER_ERROR'; - if (e.name === 'JsonWebTokenError' || e.message?.includes('token') || e.message?.includes('jwt')) { + if ( + e.name === 'JsonWebTokenError' || + e.message?.includes('token') || + e.message?.includes('jwt') + ) { detectedStatus = HttpStatus.UNAUTHORIZED; failureReason = 'INVALID_TOKEN'; issueType = 'CLIENT_ERROR'; - } else if (e.message?.includes('not found') || e.message?.includes('does not exist')) { + } else if ( + e.message?.includes('not found') || + e.message?.includes('does not exist') + ) { detectedStatus = HttpStatus.NOT_FOUND; failureReason = 'USER_NOT_FOUND'; issueType = 'CLIENT_ERROR'; - } else if (e.message?.includes('unauthorized') || e.message?.includes('forbidden')) { + } else if ( + e.message?.includes('unauthorized') || + e.message?.includes('forbidden') + ) { detectedStatus = HttpStatus.FORBIDDEN; failureReason = 'UNAUTHORIZED'; issueType = 'CLIENT_ERROR'; @@ -243,7 +252,9 @@ export class AuthService { // Log failed attempt with comprehensive details (username, userId, and IP excluded for legal compliance) LoggerUtil.error( - `GetUserByAuth failed - DetectedStatusCode: ${detectedStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId || 'Not provided'}`, + `GetUserByAuth failed - DetectedStatusCode: ${detectedStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${ + tenantId || 'Not provided' + }`, errorStack, 'AuthService', undefined // Username excluded for legal compliance diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 02ebe068..14956825 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -98,17 +98,13 @@ export class UserController { @Query('fieldvalue') fieldvalue: string | null = null ) { // Extract request information for logging - const clientIp = this.getClientIp(request); - const userAgent = request.headers['user-agent'] || 'Unknown'; let requesterUsername = 'Unknown'; - let requesterUserId = 'Unknown'; try { // Extract requester info from JWT token if available if (request.headers.authorization) { const decoded: any = jwt_decode(request.headers.authorization); requesterUsername = decoded.preferred_username || 'Unknown'; - requesterUserId = decoded.sub || 'Unknown'; } } catch (e) { // If token decode fails, log the error and continue with Unknown values (IP excluded for legal compliance) @@ -190,7 +186,7 @@ export class UserController { `GetUser failed - StatusCode: ${statusCode}, Reason: ${failureReason}, Message: ${result.message || result.error || 'Unknown error'}, IssueType: ${issueType}, TenantId: ${tenantId}`, result.error || result.message || 'Unknown error', 'UserController', - undefined // Username excluded for legal compliance + undefined ); } From 0b73e75c3469074f9b2cf095479389f86d8bd7f3 Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 18:23:00 +0530 Subject: [PATCH 6/9] Added logs for login and user read API --- src/auth/auth.service.ts | 30 ++++++++++++------------------ src/user/user.controller.ts | 29 ++++++----------------------- 2 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 34c1f0da..7a672a29 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -37,8 +37,8 @@ export class AuthService { // Log login attempt start (username and IP excluded for legal compliance) LoggerUtil.log( `Login attempt initiated - User-Agent: ${userAgent}`, - 'AuthService', - undefined, // Username excluded for legal compliance + 'AuthService', + undefined, 'info' ); @@ -54,14 +54,13 @@ export class AuthService { ? 'User details not found for user' : 'User is inactive, please verify your email'; - const failureReason = !userData ? 'USER_NOT_FOUND' : 'USER_INACTIVE'; + const failureReason = userData ? 'USER_INACTIVE' : 'USER_NOT_FOUND'; // Log failed login attempt with reason and status code (username and IP excluded for legal compliance) LoggerUtil.error( `Login failed - StatusCode: ${HttpStatus.BAD_REQUEST}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: CLIENT_ERROR`, errorMessage, - 'AuthService', - undefined + 'AuthService' ); return APIResponse.error( @@ -94,7 +93,7 @@ export class AuthService { LoggerUtil.log( `Login successful - User-Agent: ${userAgent}, StatusCode: ${HttpStatus.OK}`, 'AuthService', - undefined, // Username excluded for legal compliance + undefined, 'info' ); @@ -111,8 +110,7 @@ export class AuthService { LoggerUtil.error( `Login failed - StatusCode: ${HttpStatus.UNAUTHORIZED}, Reason: INVALID_CREDENTIALS, Message: Invalid username or password, IssueType: CLIENT_ERROR`, 'Invalid username or password', - 'AuthService', - undefined + 'AuthService' ); throw new NotFoundException('Invalid username or password'); } else { @@ -151,8 +149,7 @@ export class AuthService { LoggerUtil.error( `Login failed - StatusCode: ${httpStatus}, Reason: ${failureReason}, Message: ${errorMessage}, IssueType: ${issueType}`, errorStack, - 'AuthService', - undefined + 'AuthService' ); return APIResponse.error( @@ -171,8 +168,6 @@ export class AuthService { // Extract request information for logging const userAgent = request.headers['user-agent'] || 'Unknown'; - let username = 'Unknown'; - let userId = 'Unknown'; try { // Log API call attempt (username and IP excluded for legal compliance) @@ -181,20 +176,19 @@ export class AuthService { tenantId || 'Not provided' }`, 'AuthService', - undefined, // Username excluded for legal compliance + undefined, 'info' ); // Decode JWT token to get username const decoded: any = jwt_decode(request.headers.authorization); - username = decoded.preferred_username || 'Unknown'; - userId = decoded.sub || 'Unknown'; + const username = decoded.preferred_username || 'Unknown'; // Log with username after decoding (username, userId, and IP excluded for legal compliance) LoggerUtil.log( `GetUserByAuth processing - TenantId: ${tenantId || 'Not provided'}`, 'AuthService', - undefined, // Username excluded for legal compliance + undefined, 'info' ); @@ -206,7 +200,7 @@ export class AuthService { LoggerUtil.log( `GetUserByAuth successful - StatusCode: ${HttpStatus.OK}`, 'AuthService', - undefined, // Username excluded for legal compliance + undefined, 'info' ); @@ -257,7 +251,7 @@ export class AuthService { }`, errorStack, 'AuthService', - undefined // Username excluded for legal compliance + undefined ); // Keep original API response behavior - always return INTERNAL_SERVER_ERROR diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 14956825..30cbda85 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -97,30 +97,13 @@ export class UserController { @Param('userId', ParseUUIDPipe) userId: string, @Query('fieldvalue') fieldvalue: string | null = null ) { - // Extract request information for logging - let requesterUsername = 'Unknown'; - - try { - // Extract requester info from JWT token if available - if (request.headers.authorization) { - const decoded: any = jwt_decode(request.headers.authorization); - requesterUsername = decoded.preferred_username || 'Unknown'; - } - } catch (e) { - // If token decode fails, log the error and continue with Unknown values (IP excluded for legal compliance) - LoggerUtil.warn( - `Failed to decode JWT token for getUser request - Error: ${e?.message || 'Unknown error'}`, - 'UserController' - ); - } - const tenantId = headers['tenantid']; // Log API call attempt (username, userId, and IP excluded for legal compliance) LoggerUtil.log( `GetUser attempt - TenantId: ${tenantId || 'Not provided'}, FieldValue: ${fieldvalue || 'false'}`, 'UserController', - undefined, // Username excluded for legal compliance + undefined, 'info' ); @@ -129,8 +112,8 @@ export class UserController { LoggerUtil.error( `GetUser failed - StatusCode: 400, Reason: MISSING_TENANT_ID, Message: Missing tenantId in request headers, IssueType: CLIENT_ERROR`, 'Missing tenantId in request headers', - 'UserController', - undefined // Username excluded for legal compliance + 'UserController', + undefined ); return response .status(400) @@ -178,7 +161,7 @@ export class UserController { issueType = 'CLIENT_ERROR'; } else if (statusCode >= 500) { failureReason = 'INTERNAL_SERVER_ERROR'; - issueType = 'SERVER_ERROR'; + // issueType already defaults to 'SERVER_ERROR' for 5xx errors } // Log failed response (username, userId, and IP excluded for legal compliance) @@ -201,8 +184,8 @@ export class UserController { LoggerUtil.error( `GetUser exception - StatusCode: ${httpStatus}, Reason: EXCEPTION, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId}`, errorStack, - 'UserController', - undefined // Username excluded for legal compliance + 'UserController', + undefined ); return response.status(httpStatus).json({ From 2901ac29827344e8ac79e3d123be134df69b1bd4 Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 18:24:15 +0530 Subject: [PATCH 7/9] Added logs for login and user read API --- src/user/user.controller.ts | 45 ++++++++++++------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 30cbda85..98ce1f4d 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -98,10 +98,12 @@ export class UserController { @Query('fieldvalue') fieldvalue: string | null = null ) { const tenantId = headers['tenantid']; - + // Log API call attempt (username, userId, and IP excluded for legal compliance) LoggerUtil.log( - `GetUser attempt - TenantId: ${tenantId || 'Not provided'}, FieldValue: ${fieldvalue || 'false'}`, + `GetUser attempt - TenantId: ${tenantId || 'Not provided'}, FieldValue: ${ + fieldvalue || 'false' + }`, 'UserController', undefined, 'info' @@ -112,8 +114,8 @@ export class UserController { LoggerUtil.error( `GetUser failed - StatusCode: 400, Reason: MISSING_TENANT_ID, Message: Missing tenantId in request headers, IssueType: CLIENT_ERROR`, 'Missing tenantId in request headers', - 'UserController', - undefined + 'UserController', + undefined ); return response .status(400) @@ -135,7 +137,7 @@ export class UserController { .getUsersDetailsById(userData, response); const statusCode = result.statusCode || 200; - + // Determine if successful or failed based on status code if (statusCode >= 200 && statusCode < 300) { // Log successful response (username, userId, and IP excluded for legal compliance) @@ -149,7 +151,7 @@ export class UserController { // Log failed response with reason let failureReason = 'UNKNOWN_ERROR'; let issueType = 'SERVER_ERROR'; - + if (statusCode === 400) { failureReason = 'BAD_REQUEST'; issueType = 'CLIENT_ERROR'; @@ -166,7 +168,9 @@ export class UserController { // Log failed response (username, userId, and IP excluded for legal compliance) LoggerUtil.error( - `GetUser failed - StatusCode: ${statusCode}, Reason: ${failureReason}, Message: ${result.message || result.error || 'Unknown error'}, IssueType: ${issueType}, TenantId: ${tenantId}`, + `GetUser failed - StatusCode: ${statusCode}, Reason: ${failureReason}, Message: ${ + result.message || result.error || 'Unknown error' + }, IssueType: ${issueType}, TenantId: ${tenantId}`, result.error || result.message || 'Unknown error', 'UserController', undefined @@ -179,13 +183,13 @@ export class UserController { const errorStack = error?.stack || 'No stack trace available'; const httpStatus = error?.status || HttpStatus.INTERNAL_SERVER_ERROR; const issueType = httpStatus >= 500 ? 'SERVER_ERROR' : 'CLIENT_ERROR'; - + // Log exception with comprehensive details (username, userId, and IP excluded for legal compliance) LoggerUtil.error( `GetUser exception - StatusCode: ${httpStatus}, Reason: EXCEPTION, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId}`, errorStack, - 'UserController', - undefined + 'UserController', + undefined ); return response.status(httpStatus).json({ @@ -196,27 +200,6 @@ export class UserController { } } - /** - * Extract client IP address from request - * Handles proxy headers (X-Forwarded-For, X-Real-IP) - */ - private getClientIp(request: Request): string { - const forwarded = request.headers['x-forwarded-for']; - if (forwarded) { - // X-Forwarded-For can contain multiple IPs, take the first one - const ips = Array.isArray(forwarded) ? forwarded[0] : forwarded; - return ips.split(',')[0].trim(); - } - - const realIp = request.headers['x-real-ip']; - if (realIp) { - return Array.isArray(realIp) ? realIp[0] : realIp; - } - - // Fallback to request IP or socket remote address - return request.ip || request.socket?.remoteAddress || 'Unknown'; - } - @UseFilters(new AllExceptionsFilter(APIID.USER_CREATE)) @Post('/create') // @UseGuards(JwtAuthGuard) From 29a54c88c1d27004896a606b4aba286795b80729 Mon Sep 17 00:00:00 2001 From: Tushar Date: Fri, 2 Jan 2026 18:27:16 +0530 Subject: [PATCH 8/9] Added logs for login and user read API --- src/auth/auth.service.ts | 3 +-- src/user/user.controller.ts | 10 +++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 7a672a29..ef979178 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -250,8 +250,7 @@ export class AuthService { tenantId || 'Not provided' }`, errorStack, - 'AuthService', - undefined + 'AuthService' ); // Keep original API response behavior - always return INTERNAL_SERVER_ERROR diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 98ce1f4d..23a93a26 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -56,7 +56,6 @@ import { OtpSendDTO } from './dto/otpSend.dto'; import { OtpVerifyDTO } from './dto/otpVerify.dto'; import { UserCreateSsoDto } from './dto/user-create-sso.dto'; import { RecaptchaService } from './recaptcha.service'; -import jwt_decode from 'jwt-decode'; export interface UserData { context: string; @@ -114,8 +113,7 @@ export class UserController { LoggerUtil.error( `GetUser failed - StatusCode: 400, Reason: MISSING_TENANT_ID, Message: Missing tenantId in request headers, IssueType: CLIENT_ERROR`, 'Missing tenantId in request headers', - 'UserController', - undefined + 'UserController' ); return response .status(400) @@ -172,8 +170,7 @@ export class UserController { result.message || result.error || 'Unknown error' }, IssueType: ${issueType}, TenantId: ${tenantId}`, result.error || result.message || 'Unknown error', - 'UserController', - undefined + 'UserController' ); } @@ -188,8 +185,7 @@ export class UserController { LoggerUtil.error( `GetUser exception - StatusCode: ${httpStatus}, Reason: EXCEPTION, Message: ${errorMessage}, IssueType: ${issueType}, TenantId: ${tenantId}`, errorStack, - 'UserController', - undefined + 'UserController' ); return response.status(httpStatus).json({ From bd99e56bdfd9c947e82072df41af20dce50578ea Mon Sep 17 00:00:00 2001 From: Tushar Date: Thu, 8 Jan 2026 12:46:47 +0530 Subject: [PATCH 9/9] Fixed query for cohort search --- src/adapters/postgres/cohort-adapter.ts | 43 +++++++++++++++++-------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/adapters/postgres/cohort-adapter.ts b/src/adapters/postgres/cohort-adapter.ts index e4ef54f5..e1516417 100644 --- a/src/adapters/postgres/cohort-adapter.ts +++ b/src/adapters/postgres/cohort-adapter.ts @@ -43,7 +43,7 @@ import { FieldValueConverter } from 'src/utils/field-value-converter'; export class PostgresCohortService { // Cache for repository column names (static data) private cachedCohortColumnNames: string[] | null = null; - + // Cache for custom fields metadata (with TTL) private customFieldsCache: { data: any[]; @@ -333,8 +333,10 @@ export class PostgresCohortService { return new Map(); } + // Optimized query: Filter FieldValues first, then apply DISTINCT ON + // This avoids scanning the entire FieldValues table const query = ` - SELECT DISTINCT + SELECT fv."itemId", f."fieldId", f."label", @@ -354,17 +356,29 @@ export class PostgresCohortService { f."type", f."fieldParams", f."sourceDetails" - FROM public."Cohort" c - LEFT JOIN ( - SELECT DISTINCT ON (fv."fieldId", fv."itemId") fv.* + FROM ( + SELECT DISTINCT ON (fv."fieldId", fv."itemId") + fv."fieldId", + fv."itemId", + fv."textValue", + fv."numberValue", + fv."calendarValue", + fv."dropdownValue", + fv."radioValue", + fv."checkboxValue", + fv."textareaValue", + fv."fileValue", + fv."value" FROM public."FieldValues" fv - ) fv ON fv."itemId" = c."cohortId" + WHERE fv."itemId" = ANY($1) + ORDER BY fv."fieldId", fv."itemId", fv."createdAt" DESC, fv."fieldValuesId" DESC + ) fv INNER JOIN public."Fields" f ON fv."fieldId" = f."fieldId" - WHERE c."cohortId" = ANY($1); + ORDER BY fv."itemId", f."fieldId"; `; let results = await this.cohortMembersRepository.query(query, [cohortIds]); - + // Process results for dynamic options results = await Promise.all( results.map(async (data) => { @@ -1471,12 +1485,13 @@ export class PostgresCohortService { whereClause['cohortId'] = In(cohortIds); } - const [cohortData, totalCount] = await this.cohortRepository.findAndCount({ - where: whereClause, - order, - skip: offset, - take: limit, - }); + const [cohortData, totalCount] = + await this.cohortRepository.findAndCount({ + where: whereClause, + order, + skip: offset, + take: limit, + }); count = totalCount;