using back.DataModels; using back.services.Crypto; using Microsoft.EntityFrameworkCore; using System.Net; namespace back.context; public class UserContext : DbContext { public record HttpErrorMap(HttpStatusCode Code, string Description); public static class Errors { public static readonly HttpErrorMap Unauthorized = new(HttpStatusCode.Unauthorized, "Invalid user data. Email or password are wrong."); public static readonly HttpErrorMap BadRequest = new(HttpStatusCode.BadRequest, "Missing user data."); } public DbSet Users { get; set; } private readonly ICryptoService _cryptoService; public UserContext( DbContextOptions options, ICryptoService cryptoService ) : base(options) { _cryptoService = cryptoService ?? throw new ArgumentNullException(nameof(cryptoService)); // Ensure database is created Database.EnsureCreated(); } public async Task Create(string clientId, UserModel user) { ArgumentNullException.ThrowIfNull(user); if (await Exists(user)) { return await GetById(Guid.Parse(user.Id)) ?? null; } if (string.IsNullOrEmpty(user.Id)) { user.Id = Guid.NewGuid().ToString(); } if (string.IsNullOrEmpty(user.Salt)) { user.Salt = _cryptoService.Salt(); } user.Password = _cryptoService.Decrypt(clientId, user.Password) ?? string.Empty; user.Password = _cryptoService.Hash(user.Password + user.Salt + _cryptoService.Pepper()) ?? string.Empty; user.CreatedAt = DateTime.UtcNow; Users.Add(user); await SaveChangesAsync(); return user; } public async Task Update(UserModel user) { ArgumentNullException.ThrowIfNull(user); if (!await Exists(user)) { return null; } var existingUser = await GetById(Guid.Parse(user.Id)); if (existingUser == null) return null; existingUser.Name = user.Name; existingUser.Email = user.Email; existingUser.UpdatedAt = DateTime.UtcNow; Users.Update(existingUser); await SaveChangesAsync(); return existingUser; } public async Task Delete(Guid id) { var user = await GetById(id); if (user == null) return false; Users.Remove(user); await SaveChangesAsync(); return true; } public async Task GetByEmail(string email) { if (string.IsNullOrEmpty(email)) return null; return await Users.FirstOrDefaultAsync(u => u.Email == email); } public async Task GetUserSaltByEmail(string email) { if (string.IsNullOrEmpty(email)) return string.Empty; var user = await Users.FirstOrDefaultAsync(u => u.Email == email); return user?.Salt ?? string.Empty; } public async Task Login(string email, string password, string clientId) { if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null; var pass = _cryptoService.Decrypt(clientId, password) + await GetUserSaltByEmail(email) + _cryptoService.Pepper(); var hashedPassword = _cryptoService.Hash(pass); var user = await Users .FirstOrDefaultAsync(u => u.Email == email && u.Password == hashedPassword); return user; } public async Task GetById(Guid id) { return await Users.FindAsync(id); } public async Task GetTotalItems() { return await Users.CountAsync(); } public async Task> GetPage(int page = 1, int pageSize = 20) { if (page < 1) page = 1; if (pageSize < 1) pageSize = 20; return await Users .OrderByDescending(p => p.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); } public async Task Exists(UserModel? photo) { if (photo == null) return false; if (string.IsNullOrEmpty(photo.Id)) return false; return await Users.AnyAsync(p => p.Id == photo.Id); } public async Task Exists(string id) { return await Users.AnyAsync(p => p.Id == id); } }