Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 81 additions & 71 deletions apps/api-gateway/src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class UserController {
private readonly userService: UserService,
private readonly commonService: CommonService,
private readonly awsService: AwsService
) { }
) {}

/**
*
Expand Down Expand Up @@ -105,9 +105,9 @@ export class UserController {
return res.status(HttpStatus.OK).json(finalResponse);
}

/**
/**
* Get public profile details of a user by username.
*
*
* @param username The username of the user.
* @returns Public profile information.
*/
Expand All @@ -134,7 +134,6 @@ export class UserController {
return res.status(HttpStatus.OK).json(finalResponse);
}


/**
* Retrieves the profile details of the currently logged-in user.
*
Expand All @@ -149,7 +148,7 @@ export class UserController {
@ApiBearerAuth()
async getProfile(@User() reqUser: user, @Res() res: Response): Promise<Response> {
const userData = await this.userService.getProfile(reqUser.id);

const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: ResponseMessages.user.success.fetchProfile,
Expand All @@ -159,9 +158,9 @@ export class UserController {
return res.status(HttpStatus.OK).json(finalResponse);
}

/**
/**
* Retrieves all platform settings.
*
*
* @returns The platform settings.
*/
@Get('/platform-settings')
Expand All @@ -185,11 +184,11 @@ export class UserController {
}

/**
* Fetch user activities.
*
* @param limit - Number of activities to fetch.
* @returns A response containing user activity data.
*/
* Fetch user activities.
*
* @param limit - Number of activities to fetch.
* @returns A response containing user activity data.
*/
@Get('/activity')
@ApiOperation({
summary: 'Fetch users activity',
Expand All @@ -214,12 +213,11 @@ export class UserController {
return res.status(HttpStatus.OK).json(finalResponse);
}


/**
* Fetch organization invitations.
*
* @returns A paginated list of organization invitations.
*/
/**
* Fetch organization invitations.
*
* @returns A paginated list of organization invitations.
*/
@Get('/org-invitations')
@ApiOperation({
summary: 'organization invitations',
Expand Down Expand Up @@ -248,7 +246,7 @@ export class UserController {
required: false
})
async invitations(
@Query() getAllInvitationsDto: GetAllInvitationsDto,
@Query() getAllInvitationsDto: GetAllInvitationsDto,
@User() reqUser: user,
@Res() res: Response
): Promise<Response> {
Expand All @@ -271,14 +269,17 @@ export class UserController {
return res.status(HttpStatus.OK).json(finalResponse);
}

/**
* Checks if a user is registered and verifies email existence.
*
* @param email The email address to check.
* @returns Returns user registration and email verification status.
*/
/**
* Checks if a user is registered and verifies email existence.
*
* @param email The email address to check.
* @returns Returns user registration and email verification status.
*/
@Get('/:email')
@ApiOperation({ summary: 'Check user registration and email verification status', description: 'Check if a user is already registered and if their email already exists.' })
@ApiOperation({
summary: 'Check user registration and email verification status',
description: 'Check if a user is already registered and if their email already exists.'
})
async checkUserExist(@Param() emailParam: EmailValidator, @Res() res: Response): Promise<Response> {
const userDetails = await this.userService.checkUserExist(emailParam.email);

Expand All @@ -292,12 +293,12 @@ export class UserController {
}

/**
* Accept or reject an organization invitation.
*
* @param invitationId The ID of the organization invitation.
* @body AcceptRejectInvitationDto
* @returns The status of the organization invitation response.
*/
* Accept or reject an organization invitation.
*
* @param invitationId The ID of the organization invitation.
* @body AcceptRejectInvitationDto
* @returns The status of the organization invitation response.
*/
@Post('/org-invitations/:invitationId')
@ApiOperation({
summary: 'accept/reject organization invitation',
Expand All @@ -306,8 +307,17 @@ export class UserController {
@UseGuards(AuthGuard('jwt'), UserAccessGuard)
@ApiBearerAuth()
async acceptRejectInvitaion(
@Body() acceptRejectInvitation: AcceptRejectInvitationDto,
@Param('invitationId', TrimStringParamPipe, new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for InvitationId`); }})) invitationId: string,
@Body() acceptRejectInvitation: AcceptRejectInvitationDto,
@Param(
'invitationId',
TrimStringParamPipe,
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException(`Invalid format for InvitationId`);
}
})
)
invitationId: string,
@User() reqUser: user,
@Res() res: Response
): Promise<Response> {
Expand All @@ -320,13 +330,13 @@ export class UserController {
};
return res.status(HttpStatus.CREATED).json(finalResponse);
}

/**
* Updates the user profile.
*
* @body UpdateUserProfileDto
* @returns A response indicating the success of the update operation.
*/
* Updates the user profile.
*
* @body UpdateUserProfileDto
* @returns A response indicating the success of the update operation.
*/
@Put('/')
@ApiOperation({
summary: 'Update user profile',
Expand All @@ -343,48 +353,48 @@ export class UserController {
const userId = reqUser.id;
updateUserProfileDto.id = userId;
await this.userService.updateUserProfile(updateUserProfileDto);

const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: ResponseMessages.user.success.update
};
return res.status(HttpStatus.OK).json(finalResponse);
}

/**
/**
* @body AddPasskeyDetailsDto
* @returns User's profile update status
*/

@Put('/password/:email')
@ApiOperation({ summary: 'Store user password details', description: 'Securely store and update the user’s password details.' })
@ApiExcludeEndpoint()
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), UserAccessGuard)

async addPasskey(
@Body() userInfo: AddPasskeyDetailsDto,
@User() reqUser: user,
@Res() res: Response
): Promise<Response> {

const userDetails = await this.userService.addPasskey(reqUser.email, userInfo);
const finalResponse = {
statusCode: HttpStatus.OK,
message: ResponseMessages.user.success.update,
data: userDetails
};

return res.status(HttpStatus.OK).json(finalResponse);
}

@Put('/password/:email')
@ApiOperation({
summary: 'Store user password details',
description: 'Securely store and update the user’s password details.'
})
@ApiExcludeEndpoint()
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), UserAccessGuard)
async addPasskey(
@Body() userInfo: AddPasskeyDetailsDto,
@User() reqUser: user,
@Res() res: Response
): Promise<Response> {
const userDetails = await this.userService.addPasskey(reqUser.email, userInfo);
const finalResponse = {
statusCode: HttpStatus.OK,
message: ResponseMessages.user.success.update,
data: userDetails
};

return res.status(HttpStatus.OK).json(finalResponse);
}

/**
* Updates platform settings.
* @body UpdatePlatformSettingsDto
*
* @returns Status of the update operation.
*/
* Updates platform settings.
* @body UpdatePlatformSettingsDto
*
* @returns Status of the update operation.
*/
@Put('/platform-settings')
@ApiOperation({
summary: 'Update platform settings',
Expand All @@ -406,4 +416,4 @@ export class UserController {

return res.status(HttpStatus.OK).json(finalResponse);
}
}
}
20 changes: 9 additions & 11 deletions apps/api-gateway/src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import { NATSClient } from '@credebl/common/NATSClient';

@Injectable()
export class UserService extends BaseService {
constructor(@Inject('NATS_CLIENT') private readonly serviceProxy: ClientProxy, private readonly natsClient : NATSClient) {
constructor(
@Inject('NATS_CLIENT') private readonly serviceProxy: ClientProxy,
private readonly natsClient: NATSClient
) {
super('User Service');
}

Expand All @@ -25,8 +28,8 @@ export class UserService extends BaseService {
}

async getPublicProfile(username: string): Promise<object> {
const payload = { username };
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-public-profile', payload);
const payload = { username };
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-public-profile', payload);
}

async updateUserProfile(updateUserProfileDto: UpdateUserProfileDto): Promise<user> {
Expand All @@ -43,24 +46,19 @@ export class UserService extends BaseService {
const payload = { id };
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-by-keycloak', payload);
}

async invitations(id: string, status: string, getAllInvitationsDto: GetAllInvitationsDto): Promise<IUserInvitations> {
const { pageNumber, pageSize, search } = getAllInvitationsDto;
const payload = { id, status, pageNumber, pageSize, search };
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-org-invitations', payload);
}

async acceptRejectInvitaion(
acceptRejectInvitation: AcceptRejectInvitationDto,
userId: string
): Promise<string> {
async acceptRejectInvitaion(acceptRejectInvitation: AcceptRejectInvitationDto, userId: string): Promise<string> {
const payload = { acceptRejectInvitation, userId };
return this.natsClient.sendNatsMessage(this.serviceProxy, 'accept-reject-invitations', payload);
}

async get(
paginationDto:PaginationDto
): Promise<object> {
async get(paginationDto: PaginationDto): Promise<object> {
const { pageNumber, pageSize, search } = paginationDto;
const payload = { pageNumber, pageSize, search };
return this.natsClient.sendNatsMessage(this.serviceProxy, 'fetch-users', payload);
Expand Down
11 changes: 10 additions & 1 deletion apps/organization/src/organization.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ import { DeleteOrgInvitationsEmail } from '../templates/delete-organization-invi
import { IOrgRoles } from 'libs/org-roles/interfaces/org-roles.interface';
import { NATSClient } from '@credebl/common/NATSClient';
import { UserRepository } from 'apps/user/repositories/user.repository';
import * as jwt from 'jsonwebtoken';

@Injectable()
export class OrganizationService {
constructor(
Expand Down Expand Up @@ -708,17 +710,24 @@ export class OrganizationService {
let addSessionDetails;
// Fetch owner organization details for getting the user id
const orgRoleDetails = await this.organizationRepository.getOrgAndOwnerUser(clientId);
// called seprate method to delete exp session
this.userRepository.deleteInactiveSessions(orgRoleDetails['user'].id);

// Fetch the total number of sessions for the requested user to check and restrict the creation of multiple sessions.
const userSessionDetails = await this.userRepository.fetchUserSessions(orgRoleDetails['user'].id);
if (Number(process.env.SESSIONS_LIMIT) <= userSessionDetails?.length) {
throw new BadRequestException(ResponseMessages.user.error.sessionLimitReached);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const decodedToken: any = jwt.decode(authenticationResult?.access_token);
const expiresAt = new Date(decodedToken.exp * 1000);
// Session payload
const sessionData = {
sessionToken: authenticationResult?.access_token,
userId: orgRoleDetails['user'].id,
expires: authenticationResult?.expires_in,
sessionType: SessionType.ORG_SESSION
sessionType: SessionType.ORG_SESSION,
expiresAt
};
// Note:
// Fetch account details to check whether the requested user account exists
Expand Down
1 change: 1 addition & 0 deletions apps/user/interfaces/user.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export interface ISession {
type?: string;
accountId?: string;
sessionType?: string;
expiresAt?: Date;
}

export interface IUpdateAccountDetails {
Expand Down
23 changes: 21 additions & 2 deletions apps/user/repositories/user.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ export class UserRepository {

async createSession(tokenDetails: ISession): Promise<session> {
try {
const { sessionToken, userId, expires, refreshToken, accountId, sessionType } = tokenDetails;
const { sessionToken, userId, expires, refreshToken, accountId, sessionType, expiresAt } = tokenDetails;
const sessionResponse = await this.prisma.session.create({
data: {
id: tokenDetails.id,
Expand All @@ -687,7 +687,8 @@ export class UserRepository {
userId,
refreshToken,
accountId,
sessionType
sessionType,
expiresAt
}
});
return sessionResponse;
Expand Down Expand Up @@ -987,4 +988,22 @@ export class UserRepository {
throw error;
}
}

async deleteInactiveSessions(userId: string): Promise<Prisma.BatchPayload> {
try {
const response = await this.prisma.session.deleteMany({
where: {
expiresAt: {
lt: new Date()
},
userId
}
});
this.logger.debug('Deleted inactive sessions::', response);
return response;
} catch (error) {
this.logger.error(`Error in deleting the in active sessions::${error.message}`);
throw error;
}
}
}
Loading