using Azaion.Common; using Azaion.Common.Database; using Azaion.Common.Entities; using Azaion.Common.Extensions; using Azaion.Common.Requests; using LinqToDB; using Newtonsoft.Json; namespace Azaion.Services; public interface IUserService { Task RegisterUser(RegisterUserRequest request, CancellationToken cancellationToken = default); Task ValidateUser(LoginRequest request, CancellationToken cancellationToken = default); Task GetByEmail(string? email, CancellationToken cancellationToken = default); Task UpdateHardware(string email, HardwareInfo? hardwareInfo = null, CancellationToken cancellationToken = default); Task UpdateQueueOffsets(string email, UserQueueOffsets queueOffsets, CancellationToken cancellationToken = default); Task> GetUsers(string? searchEmail, RoleEnum? searchRole, CancellationToken cancellationToken); Task CheckHardwareHash(User user, GetResourceRequest request); } public class UserService(IDbFactory dbFactory, ICache cache) : IUserService { public async Task RegisterUser(RegisterUserRequest request, CancellationToken cancellationToken = default) { await dbFactory.RunAdmin(async db => { var existingUser = await db.Users.FirstOrDefaultAsync(u => u.Email == request.Email, token: cancellationToken); 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 }, token: cancellationToken); }); } public async Task GetByEmail(string? email, CancellationToken cancellationToken = default) => await cache.GetFromCacheAsync(User.GetCacheKey(email), async () => await dbFactory.Run(async db => await db.Users.FirstOrDefaultAsync(x => x.Email == email, cancellationToken))); public async Task ValidateUser(LoginRequest request, CancellationToken cancellationToken = default) => await dbFactory.Run(async db => { var user = await db.Users.FirstOrDefaultAsync(x => x.Email == request.Email, token: cancellationToken); 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, HardwareInfo? hardware = null, CancellationToken cancellationToken = default) { await dbFactory.RunAdmin(async db => { var hardwareStr = hardware == null ? "" : JsonConvert.SerializeObject(hardware); await db.Users.UpdateAsync(x => x.Email == email, u => new User { Hardware = hardwareStr }, token: cancellationToken); }); cache.Invalidate(User.GetCacheKey(email)); } public async Task UpdateQueueOffsets(string email, UserQueueOffsets queueOffsets, CancellationToken cancellationToken = default) { await dbFactory.RunAdmin(async db => { var userConfig = await db.Users.Where(x => x.Email == email).Select(x => x.UserConfig).FirstOrDefaultAsync(token: cancellationToken); userConfig ??= new UserConfig(); userConfig.QueueOffsets = queueOffsets; await db.Users.UpdateAsync(x => x.Email == email, u => new User { UserConfig = userConfig }, token: cancellationToken); }); cache.Invalidate(User.GetCacheKey(email)); } public async Task> GetUsers(string? searchEmail, RoleEnum? searchRole, CancellationToken cancellationToken) => 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: cancellationToken)); public async Task CheckHardwareHash(User user, GetResourceRequest request) { var requestHWHash = Security.GetHWHash(request.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, request.Hardware); cache.Invalidate(User.GetCacheKey(user.Email)); return requestHWHash; } var userHW = JsonConvert.DeserializeObject(user.Hardware); var userHWHash = Security.GetHWHash(userHW!); if (userHWHash != requestHWHash) throw new BusinessException(ExceptionEnum.HardwareIdMismatch); return userHWHash; } }