namespace backend.Services; using backend.Data; using backend.DTOs; using backend.Email; using backend.Entities; using backend.Helpers; using backend.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; public interface IUserService { Task Authenticate(AuthenticateRequest model); Task Register(CreateUserRequest req, bool isFirst); Task GetById(int id); Task GetByEmail(string email); Task SendEmailConfirmation(string email); Task SendPasswordReset(string email); Task ConfirmEmail(User user, string checkValid); Task ResetPassword(string checkValid, string password); Task> GetUsers(); Task UpdateVipStatus(int id, bool isVip); } public class UserService : IUserService { private readonly AppSettings _appSettings; private readonly DataContext _context = null; private readonly IEmailSender _emailSender; public UserService(IOptions appSettings, DataContext context, IEmailSender emailSender) { _appSettings = appSettings.Value; _context = context; _emailSender = emailSender; } public async Task GetByEmail(string email) { return await _context.Users.FirstOrDefaultAsync(x => x.Email == email); } public async Task Authenticate(AuthenticateRequest model) { User user = await _context.Users.FirstOrDefaultAsync(x => x.Email == model.Email && x.Password == model.Password); // return null if user not found if (user == null) return null; // authentication successful so generate jwt token var token = generateJwtToken(user); return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = user.IsConfirmed, isVip = user.IsVip}; } public async Task ConfirmEmail(User user, string checkValid) { if(user.ConfirmationURL != checkValid) { throw new Exception("Invalid check"); } if(user.ConfirmationValidTo < DateTime.UtcNow) { throw new Exception("Link expired"); } user.IsConfirmed = true; _context.Users.Update(user); await _context.SaveChangesAsync(); } public async Task GetById(int id) { return await _context.Users.FindAsync(id); } public async Task Register(CreateUserRequest req, bool isFirst) { var exists = await _context.Users.FirstOrDefaultAsync(x => x.Email == req.Email); if(exists != null && req.IsConfirmed) { return new AuthenticateResponse { Email = exists.Email, Id = exists.Id, IsAdmin = exists.IsAdmin, IsConfirmed = true }; } User user = new User() { Email = req.Email, Password = req.Password, IsAdmin = isFirst, IsConfirmed = req.IsConfirmed, IsVip = false }; await _context.Users.AddAsync(user); await _context.SaveChangesAsync(); var token = generateJwtToken(user); return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = req.IsConfirmed, isVip = user.IsVip }; } public async Task ResetPassword(string checkValid, string password) { var user = await _context.Users.Where(x => x.PasswordResetURL == checkValid).FirstOrDefaultAsync(); if (user == null) { throw new Exception("Invalid check"); } if (user.PasswordResetValidTo < DateTime.UtcNow) { throw new Exception("Link expired"); } user.Password = password; _context.Users.Update(user); await _context.SaveChangesAsync(); } public async Task SendEmailConfirmation(string email) { User user = await _context.Users.FirstOrDefaultAsync(x => x.Email == email); user.ConfirmationURL = Guid.NewGuid().ToString(); user.ConfirmationValidTo = DateTime.UtcNow.AddHours(24); _context.Users.Update(user); await _context.SaveChangesAsync(); await _emailSender.SendEmailAsync( "Потврдете го вашиот емаил", "Ве молиме кликнете на следниот линк за да го потврдите вашиот емаил: http://localhost:3000/confirm?id=" + user.ConfirmationURL, email); } public async Task SendPasswordReset(string email) { User user = await _context.Users.FirstOrDefaultAsync(x => x.Email == email); user.PasswordResetURL = Guid.NewGuid().ToString(); user.PasswordResetValidTo = DateTime.UtcNow.AddHours(24); _context.Users.Update(user); await _context.SaveChangesAsync(); await _emailSender.SendEmailAsync( "Ресетирајте ја лозинката", "Ве молиме кликнете на следниот линк за да ја ресетирате лозинката: http://localhost:3000/reset?id=" + user.PasswordResetURL, email); } public async Task> GetUsers() { return await _context.Users.Select(x => new UserResponse() { Email = x.Email, Id = x.Id, IsVip = x.IsVip }).OrderBy(x => x.Id).ToListAsync(); } public async Task UpdateVipStatus(int id, bool isVip) { var user = await _context.Users.FindAsync(id); user.IsVip = isVip; _context.Users.Update(user); await _context.SaveChangesAsync(); } private string generateJwtToken(User user) { // generate token that is valid for 7 days var tokenHandler = new JwtSecurityTokenHandler(); var key = System.Text.Encoding.ASCII.GetBytes(_appSettings.Secret); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("id", user.Id.ToString()) }), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } private string sha256Hash(String value) { using (SHA256 hash = SHA256.Create()) { return String.Concat(hash .ComputeHash(Encoding.UTF8.GetBytes(value)) .Select(item => item.ToString("x2"))); } } }