Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 7x 7x 7x 7x 7x 7x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 4x 4x 3x 3x 4x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 1x | import { Injectable } from '@nestjs/common';
import { EventBus } from '@nestjs/cqrs';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User, type Company } from '@amalia/core/models';
import { assert } from '@amalia/ext/typescript';
import { IdentityProviderService } from '@amalia/kernel/auth/core';
import { type AuthenticatedContext } from '@amalia/kernel/auth/types';
import { RecordObject, RecordType } from '@amalia/tenants/monitoring/audit/types';
import { AvatarsService } from '@amalia/tenants/users/profile/core';
import { formatUserFullName, type UserContract } from '@amalia/tenants/users/types';
import { AppUsersRepository } from '../appUsers.repository';
import { UserSignedInEvent } from '../events/UserSignedInEvent';
@Injectable()
export class UpdateUserAfterConnectionUseCase {
public constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
private readonly eventBus: EventBus,
private readonly appUsersRepository: AppUsersRepository,
private readonly identityProviderService: IdentityProviderService,
private readonly avatarsService: AvatarsService,
) {}
public async execute({
company,
authenticatedContext,
token,
}: {
company: Company;
authenticatedContext: AuthenticatedContext;
token: string;
}) {
const userFromDb = await this.appUsersRepository.findUserByEmail(authenticatedContext.user.email);
assert(userFromDb, 'User not found');
if (authenticatedContext.impersonation) {
this.eventBus.publish(
new UserSignedInEvent(company, userFromDb, {
authenticatedContext: { ...authenticatedContext, user: userFromDb },
object: RecordObject.AUTHENTICATION,
type: RecordType.LOG,
values: {
target: {
id: userFromDb.id,
name: `${formatUserFullName(userFromDb)} (impersonated)`,
},
newValues: {
action: 'Sign in',
impersonator: authenticatedContext.impersonation.impersonator.email,
},
},
}),
);
return {
previousLastConnection: null,
user: userFromDb as UserContract,
};
}
// If the user is not an impersonation, we're going to update his last connection date, and his avatar if needed.
// If it's my first connection, log the joinedAt.
if (!userFromDb.joinedAt) {
userFromDb.joinedAt = new Date();
}
// User could have uploaded its own avatar.
// We fall back to the one from auth0 if the user doesn't have one, or if it's the default one.
const shouldUpdatePicture = !userFromDb.pictureURL || this.avatarsService.isDefaultAvatar(userFromDb.pictureURL);
if (shouldUpdatePicture) {
const { picture: auth0Picture } = await this.identityProviderService.getUserInfo(token);
if (auth0Picture) {
userFromDb.pictureURL = auth0Picture;
}
}
// Update the last connection date.
const previousLastConnection = userFromDb.lastConnection || null;
userFromDb.lastConnection = new Date();
await Promise.all([
// Save the user with updated fields.
this.userRepository.save(userFromDb),
// Log the connection.
this.eventBus.publish(
new UserSignedInEvent(company, userFromDb, {
authenticatedContext: {
...authenticatedContext,
user: userFromDb,
},
object: RecordObject.AUTHENTICATION,
type: RecordType.LOG,
values: {
target: {
id: userFromDb.id,
name: formatUserFullName(userFromDb),
},
newValues: {
action: 'Sign in',
},
},
}),
),
]);
return { previousLastConnection, user: userFromDb };
}
}
|