efficiency & performance fine tuning

This commit is contained in:
2025-12-18 14:24:00 +08:00
parent fdd8a693e2
commit e3b56781e1
14 changed files with 392 additions and 224 deletions

View File

@@ -9,10 +9,12 @@ describe('AuthService', () => {
const mockCreateFromToken = jest.fn();
const mockFindByKeycloakSub = jest.fn();
const mockFindOrCreate = jest.fn();
const mockUsersService = {
createFromToken: mockCreateFromToken,
findByKeycloakSub: mockFindByKeycloakSub,
findOrCreate: mockFindOrCreate,
};
const mockAuthUser: AuthenticatedUser = {
@@ -205,25 +207,13 @@ describe('AuthService', () => {
});
describe('ensureUserExists', () => {
it('should return existing user without updating', async () => {
mockFindByKeycloakSub.mockResolvedValue(mockUser);
it('should call findOrCreate with correct params', async () => {
mockFindOrCreate.mockResolvedValue(mockUser);
const result = await service.ensureUserExists(mockAuthUser);
expect(result).toEqual(mockUser);
expect(mockFindByKeycloakSub).toHaveBeenCalledWith('f:realm:user123');
expect(mockCreateFromToken).not.toHaveBeenCalled();
});
it('should create new user if user does not exist', async () => {
mockFindByKeycloakSub.mockResolvedValue(null);
mockCreateFromToken.mockResolvedValue(mockUser);
const result = await service.ensureUserExists(mockAuthUser);
expect(result).toEqual(mockUser);
expect(mockFindByKeycloakSub).toHaveBeenCalledWith('f:realm:user123');
expect(mockCreateFromToken).toHaveBeenCalledWith({
expect(mockFindOrCreate).toHaveBeenCalledWith({
keycloakSub: 'f:realm:user123',
email: 'test@example.com',
name: 'Test User',
@@ -233,14 +223,13 @@ describe('AuthService', () => {
});
});
it('should handle user with no email when creating new user', async () => {
it('should handle user with no email', async () => {
const authUserNoEmail: AuthenticatedUser = {
keycloakSub: 'f:realm:user456',
name: 'No Email User',
};
mockFindByKeycloakSub.mockResolvedValue(null);
mockCreateFromToken.mockResolvedValue({
mockFindOrCreate.mockResolvedValue({
...mockUser,
email: '',
name: 'No Email User',
@@ -248,8 +237,7 @@ describe('AuthService', () => {
await service.ensureUserExists(authUserNoEmail);
expect(mockFindByKeycloakSub).toHaveBeenCalledWith('f:realm:user456');
expect(mockCreateFromToken).toHaveBeenCalledWith(
expect(mockFindOrCreate).toHaveBeenCalledWith(
expect.objectContaining({
email: '',
name: 'No Email User',
@@ -262,16 +250,14 @@ describe('AuthService', () => {
keycloakSub: 'f:realm:minimal',
};
mockFindByKeycloakSub.mockResolvedValue(null);
mockCreateFromToken.mockResolvedValue({
mockFindOrCreate.mockResolvedValue({
...mockUser,
name: 'Unknown User',
});
await service.ensureUserExists(authUserMinimal);
expect(mockFindByKeycloakSub).toHaveBeenCalledWith('f:realm:minimal');
expect(mockCreateFromToken).toHaveBeenCalledWith(
expect(mockFindOrCreate).toHaveBeenCalledWith(
expect.objectContaining({
name: 'Unknown User',
}),

View File

@@ -72,7 +72,7 @@ export class AuthService {
* need to sync profile data from Keycloak on every request.
*
* - For new users: Creates with full profile data
* - For existing users: Returns existing record WITHOUT updating
* - For existing users: Returns existing record WITHOUT updating (read-only)
*
* Use this for most API endpoints. Only use syncUserFromToken() for actual
* login events (WebSocket connections, /users/me endpoint).
@@ -84,20 +84,10 @@ export class AuthService {
const { keycloakSub, email, name, username, picture, roles } =
authenticatedUser;
// Check if user exists
const existingUser = await this.usersService.findByKeycloakSub(keycloakSub);
if (existingUser) {
// User exists - return without updating
return existingUser;
}
// New user - create with full profile data
this.logger.log(
`Creating new user from token: ${keycloakSub} (via ensureUserExists)`,
);
const user = await this.usersService.createFromToken({
// Use optimized findOrCreate method
// This returns existing users immediately (1 read)
// And creates new users if needed (1 read + 1 write)
return await this.usersService.findOrCreate({
keycloakSub,
email: email || '',
name: name || username || 'Unknown User',
@@ -105,8 +95,6 @@ export class AuthService {
picture,
roles,
});
return user;
}
/**