using Azaion.Common; using Azaion.Common.Database; using Azaion.Common.Entities; using Azaion.Common.Extensions; using Azaion.Common.Requests; using LinqToDB; namespace Azaion.Services; public interface IUserService { Task RegisterUser(RegisterUserRequest request, CancellationToken ct = default); Task ValidateUser(LoginRequest request, CancellationToken ct = default); Task GetByEmail(string? email, CancellationToken ct = default); Task UpdateHardware(string email, string? hardware = null, CancellationToken ct = default); Task UpdateQueueOffsets(string email, UserQueueOffsets queueOffsets, CancellationToken ct = default); Task> GetUsers(string? searchEmail, RoleEnum? searchRole, CancellationToken ct = default); Task CheckHardwareHash(User user, string hardware, CancellationToken ct = default); Task ChangeRole(string email, RoleEnum newRole, CancellationToken ct = default); Task SetEnableStatus(string email, bool isEnabled, CancellationToken ct = default); Task RemoveUser(string email, CancellationToken ct = default); } public class UserService(IDbFactory dbFactory, ICache cache) : IUserService { public async Task RegisterUser(RegisterUserRequest request, CancellationToken ct = default) { await dbFactory.RunAdmin(async db => { var existingUser = await db.Users.FirstOrDefaultAsync(u => u.Email == request.Email, token: ct); if (existingUser != null) throw new BusinessException(ExceptionEnum.EmailExists); await db.InsertAsync(new User { Id = Guid.NewGuid(), Email = request.Email, PasswordHash = request.Password.ToHash(), Role = request.Role, CreatedAt = DateTime.UtcNow, IsEnabled = true }, token: ct); }); } public async Task GetByEmail(string? email, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(email)) throw new ArgumentNullException(nameof(email)); return await cache.GetFromCacheAsync(User.GetCacheKey(email), async () => await dbFactory.Run(async db => await db.Users.FirstOrDefaultAsync(x => x.Email == email, ct))); } public async Task ValidateUser(LoginRequest request, CancellationToken ct = default) => await dbFactory.Run(async db => { var user = await db.Users.FirstOrDefaultAsync(x => x.Email == request.Email, token: ct); if (user == null) throw new BusinessException(ExceptionEnum.NoEmailFound); if (request.Password.ToHash() != user.PasswordHash) throw new BusinessException(ExceptionEnum.WrongPassword); return user; }); public async Task UpdateHardware(string email, string? hardware = null, CancellationToken ct = default) { await dbFactory.RunAdmin(async db => { await db.Users.UpdateAsync(x => x.Email == email, u => new User { Hardware = hardware }, token: ct); }); cache.Invalidate(User.GetCacheKey(email)); } public async Task UpdateQueueOffsets(string email, UserQueueOffsets queueOffsets, CancellationToken ct = default) { await dbFactory.RunAdmin(async db => { var userConfig = await db.Users.Where(x => x.Email == email).Select(x => x.UserConfig).FirstOrDefaultAsync(token: ct); userConfig ??= new UserConfig(); userConfig.QueueOffsets = queueOffsets; await db.Users.UpdateAsync(x => x.Email == email, u => new User { UserConfig = userConfig }, token: ct); }); cache.Invalidate(User.GetCacheKey(email)); } public async Task> GetUsers(string? searchEmail, RoleEnum? searchRole, CancellationToken ct) => await dbFactory.Run(async db => await db.Users .WhereIf(!string.IsNullOrEmpty(searchEmail), u => u.Email.ToLower().Contains(searchEmail!.ToLower())) .WhereIf(searchRole != null, u => u.Role == searchRole) .ToListAsync(token: ct)); public async Task CheckHardwareHash(User user, string hardware, CancellationToken ct = default) { var requestHWHash = Security.GetHWHash(hardware); //For the new users Hardware would be empty, fill it with actual hardware on the very first request if (string.IsNullOrEmpty(user.Hardware)) { await UpdateHardware(user.Email, hardware, ct); cache.Invalidate(User.GetCacheKey(user.Email)); await UpdateLastLoginDate(user, ct); return requestHWHash; } var userHWHash = Security.GetHWHash(user.Hardware); if (userHWHash != requestHWHash) throw new BusinessException(ExceptionEnum.HardwareIdMismatch); await UpdateLastLoginDate(user, ct); return userHWHash; } private async Task UpdateLastLoginDate(User user, CancellationToken ct = default) { await dbFactory.RunAdmin(async db => await db.Users.UpdateAsync(x => x.Email == user.Email, u => new User { LastLogin = DateTime.UtcNow }, ct)); } public async Task ChangeRole(string email, RoleEnum newRole, CancellationToken ct = default) { await dbFactory.RunAdmin(async db => await db.Users.UpdateAsync(x => x.Email == email, u => new User { Role = newRole }, ct)); } public async Task SetEnableStatus(string email, bool isEnabled, CancellationToken ct = default) { await dbFactory.RunAdmin(async db => await db.Users.UpdateAsync(x => x.Email == email, u => new User { IsEnabled = isEnabled }, ct)); } public async Task RemoveUser(string email, CancellationToken ct = default) { await dbFactory.RunAdmin(async db => await db.Users.DeleteAsync(x => x.Email == email, ct)); } }