using av_motion_api.Data; using av_motion_api.Models; using av_motion_api.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System.Data; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using System.Net; using System.Net.Mail; using Microsoft.AspNetCore.Cors; using System.Text.RegularExpressions; using SendGrid.Helpers.Mail; using SendGrid; namespace av_motion_api.Controllers { [Route("api/[controller]")] [EnableCors("AllowAll")] [ApiController] public class UserController : ControllerBase { private readonly UserManager<User> _userManager; private readonly IUserClaimsPrincipalFactory<User> _claimsPrincipalFactory; private readonly IConfiguration _configuration; private readonly AppDbContext _appDbContext; private readonly RoleManager<Role> _roleManager; private readonly ILogger<UserController> _logger; public UserController(AppDbContext context, UserManager<User> userManager, IUserClaimsPrincipalFactory<User> claimsPrincipalFactory, IConfiguration configuration, RoleManager<Role> roleManager, ILogger<UserController> logger) { _appDbContext = context; _userManager = userManager; _claimsPrincipalFactory = claimsPrincipalFactory; _configuration = configuration; _roleManager = roleManager; _logger = logger; } ////addUser //[HttpPost] //[Route("Register")] //public async Task<IActionResult> Register(UserViewModel uvm) //{ // var user = await _userManager.FindByEmailAsync(uvm.Email); // int lastUserID = _appDbContext.Users // .OrderByDescending(u => u.User_ID) // .Select(u => u.User_ID) // .FirstOrDefault(); // if (user == null) // { // user = new User // { // User_ID = lastUserID + 1, // Name = uvm.Name, // Surname = uvm.Surname, // UserName = uvm.Email, // Email = uvm.Email, // PasswordHash = uvm.Password, // User_Status_ID = uvm.User_Status_ID, // User_Type_ID = uvm.User_Type_ID, // PhoneNumber = uvm.PhoneNumber, // Date_of_Birth = uvm.Date_of_Birth, // ID_Number = uvm.Id_Number, // Physical_Address = uvm.Physical_Address, // Photo = uvm.Photo // }; // IdentityResult result = await _userManager.CreateAsync(user, uvm.Password); // if (result.Succeeded) // { // // Assign role based on User_Type_ID // string roleName = GetRoleNameByUserType(uvm.User_Type_ID); // if (!string.IsNullOrEmpty(roleName)) // { // var roleResult = await _userManager.AddToRoleAsync(user, roleName); // if (!roleResult.Succeeded) // { // return BadRequest(roleResult.Errors); // } // } // } // else // { // return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support."); // } // } // else // { // return Forbid("Account already exists."); // } // return Ok(); //} //private string GetRoleNameByUserType(int userTypeId) //{ // return userTypeId switch // { // 1 => "Administrator", // 2 => "Employee", // 3 => "Member", // _ => string.Empty, // }; //} //addUser [HttpPost] [DisableRequestSizeLimit] [Route("Register")] public async Task<IActionResult> Register([FromForm] UserViewModel uvm) { try { var formCollection = await Request.ReadFormAsync(); var photo = formCollection.Files.FirstOrDefault(); var user = await _userManager.FindByEmailAsync(uvm.Email); int lastUserID = _appDbContext.Users .OrderByDescending(u => u.User_ID) .Select(u => u.User_ID) .FirstOrDefault(); // Validate Phone Number Pattern var phoneNumberPattern = @"^\d{10}$"; bool isValidPhoneNumber = Regex.IsMatch(uvm.PhoneNumber, phoneNumberPattern); if (!isValidPhoneNumber) return BadRequest("Enter valid 10-digit phone number."); // Validate South African ID number if (!IsValidSouthAfricanIDNumber(uvm.Id_Number, uvm.Date_of_Birth)) { return BadRequest("Enter a valid South African ID number."); } if (user == null) { if (photo != null && photo.Length > 0) { using (var memoryStream = new MemoryStream()) { await photo.CopyToAsync(memoryStream); var fileBytes = memoryStream.ToArray(); string base64Image = Convert.ToBase64String(fileBytes); user = new User { User_ID = lastUserID + 1, Name = uvm.Name, Surname = uvm.Surname, UserName = uvm.Email, Email = uvm.Email, PasswordHash = _userManager.PasswordHasher.HashPassword(null, uvm.Password), User_Status_ID = uvm.User_Status_ID, User_Type_ID = uvm.User_Type_ID, PhoneNumber = uvm.PhoneNumber, Date_of_Birth = uvm.Date_of_Birth, ID_Number = uvm.Id_Number, Physical_Address = uvm.Physical_Address, Photo = base64Image // Store the base64 string of the photo }; IdentityResult result = await _userManager.CreateAsync(user); if (result.Succeeded) { // Assign role based on User_Type_ID string roleName = GetRoleNameByUserType(uvm.User_Type_ID); if (!string.IsNullOrEmpty(roleName)) { var roleResult = await _userManager.AddToRoleAsync(user, roleName); if (!roleResult.Succeeded) { return BadRequest(new { Status = "Error", Errors = roleResult.Errors }); } } return Ok(new { Status = "Success", Message = "Your profile has been created successfully!" }); } else { return StatusCode(StatusCodes.Status500InternalServerError, result.Errors.FirstOrDefault()?.Description); } } } else { return BadRequest("Photo is required."); } } else { return Forbid("User already exists."); } } catch (DbUpdateException dbEx) { return StatusCode(StatusCodes.Status500InternalServerError, dbEx.InnerException?.Message ?? "An error occurred while processing your request."); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while processing your request."); } } // Method to validate South African ID number private bool IsValidSouthAfricanIDNumber(string idNumber, DateTime dateOfBirth) { // Check if the ID number is exactly 13 digits long if (idNumber.Length != 13 || !long.TryParse(idNumber, out _)) { return false; } // Validate date of birth (first six digits) string dateOfBirthPart = idNumber.Substring(0, 6); if (!DateTime.TryParseExact(dateOfBirthPart, "yyMMdd", null, System.Globalization.DateTimeStyles.None, out DateTime parsedDate)) { return false; } // Check if the last two digits of the ID number match the last two digits of the year of birth if (parsedDate.Year % 100 != dateOfBirth.Year % 100) { return false; } // If it passes the length, date of birth, and year checks, it is considered valid return true; } private string GetRoleNameByUserType(int userTypeId) { return userTypeId switch { 1 => "Administrator", 2 => "Employee", 3 => "Member", _ => string.Empty, }; } [HttpPost] [Route("Login")] public async Task<ActionResult> Login(LoginViewModel lv) { var user = await _userManager.FindByNameAsync(lv.Email); if (user != null && await _userManager.CheckPasswordAsync(user, lv.Password)) { try { var principal = await _claimsPrincipalFactory.CreateAsync(user); return await GenerateJWTToken(user); } catch (Exception) { return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support."); } } else { return NotFound("Incorrect email or password, Please Try Again"); } } [HttpGet] private async Task<ActionResult> GenerateJWTToken(User user) { var role = await _userManager.GetRolesAsync(user); IdentityOptions _identityOptions = new IdentityOptions(); // Create JWT Token var claims = new List<Claim> { new Claim(JwtRegisteredClaimNames.Sub, user.Email), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName), // Add user ID claim new Claim("userId", user.Id.ToString()), new Claim("User_Type_ID", user.User_Type_ID.ToString()), }; if (role.Count() > 0) { claims.Add(new Claim(_identityOptions.ClaimsIdentity.RoleClaimType, role.FirstOrDefault())); } var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"])); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _configuration["Tokens:Issuer"], audience: _configuration["Tokens:Audience"], claims: claims, signingCredentials: credentials, expires: DateTime.UtcNow.AddHours(3) ); return Created("", new { token = new JwtSecurityTokenHandler().WriteToken(token), user = user.UserName, userTypeId = user.User_Type_ID, // Include user ID in the response userId = user.Id }); } [HttpPost] [Route("ChangePassword")] public async Task<IActionResult> ChangePassword(int id, ChangePasswordViewModel cpvm) { var user = await _userManager.FindByIdAsync(id.ToString()); if (user == null) { return NotFound("User not found."); } var result = await _userManager.ChangePasswordAsync(user, cpvm.CurrentPassword, cpvm.NewPassword); if (result.Succeeded) { return Ok("Password changed successfully."); } else { return BadRequest(result.Errors); } } [HttpPut] [Route("editUser/{id}")] public async Task<IActionResult> EditUser(int id, [FromForm] UpdateUserViewModel uv) { try { var user = await _userManager.FindByIdAsync(id.ToString()); if (user == null) { return NotFound("User not found."); } // Read the form data to get the photo file var formCollection = await Request.ReadFormAsync(); var photo = formCollection.Files.FirstOrDefault(); user.Name = uv.Name; user.Surname = uv.Surname; user.Email = uv.Email; user.Physical_Address = uv.Physical_Address; user.PhoneNumber = uv.PhoneNumber; if (photo != null && photo.Length > 0) { using (var memoryStream = new MemoryStream()) { await photo.CopyToAsync(memoryStream); var fileBytes = memoryStream.ToArray(); string base64Image = Convert.ToBase64String(fileBytes); user.Photo = base64Image; // Store the base64 string of the photo } } // Update the user var result = await _userManager.UpdateAsync(user); if (result.Succeeded) { return Ok("User updated successfully."); } else { return BadRequest(result.Errors); } } catch (Exception) { return BadRequest("An Error Occurred, Please Try Again"); } } [HttpDelete] [Route("deleteUser/{id}")] public async Task<IActionResult> DeleteUser(int id) { try { var user = await _userManager.FindByIdAsync(id.ToString()); if (user == null) { return NotFound("User not found."); } var result = await _userManager.DeleteAsync(user); if (result.Succeeded) { return Ok(); } else { return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support."); } } catch (Exception) { return BadRequest("An Error Occured, Please Try Again"); } } [HttpGet] [Route("getAllUsers")] public IActionResult GetAllUsers() { try { var users = _userManager.Users.ToList(); if (users == null || users.Count == 0) { return NotFound("No users found."); } return Ok(users); } catch (Exception) { return BadRequest("An Error Occured, Please Try Again"); } } [HttpGet] [Route("getUserById/{id}")] public async Task<IActionResult> GetUserById(int id) { try { var u = await _appDbContext.Users .Include(u => u.User_Status) .Include(u => u.User_Type) .FirstOrDefaultAsync(u => u.Id == id); var user = new { u.Id, u.Name, u.Surname, u.Email, u.Physical_Address, u.PhoneNumber, u.Date_of_Birth, UserStatus = u.User_Status.User_Status_Description, UserType = u.User_Type.User_Type_Name, u.Photo, u.ID_Number }; return Ok(user); } catch (Exception ex) { // Log the exception for debugging Console.WriteLine(ex.Message); return BadRequest("An error occurred while fetching user details."); } } [HttpGet("GetMemberByUserId/{userId}")] public async Task<ActionResult<Member>> GetMemberByUserId(int userId) { var member = await _appDbContext.Members.FirstOrDefaultAsync(m => m.User_ID == userId); if (member == null) { return NotFound(); } return Ok(member); } [HttpGet("employee")] public async Task<ActionResult<IEnumerable<EmployeeViewModel>>> GetEmployees() { var query = await( from e in _appDbContext.Employees join u in _appDbContext.Users on e.User_ID equals u.User_ID select new EmployeeViewModel { employee_ID = e.Employee_ID, employee_name = u.Name }).ToListAsync(); return query; } //Roles [HttpPost] [Route("CreateRole")] public async Task<IActionResult> CreateRole(string roleName) { var role = await _roleManager.FindByNameAsync(roleName); if (role == null) { role = new Role { Name = roleName, NormalizedName = roleName.ToUpper(), isEditable = true, }; var result = await _roleManager.CreateAsync(role); if (!result.Succeeded) return BadRequest(result.Errors); } else { return Forbid("Role already exists."); } return Ok(); } [HttpPost] [Route("AssignRole")] public async Task<IActionResult> AssignRole(string emailAddress, string roleName) { var user = await _userManager.FindByEmailAsync(emailAddress); if (user == null) return NotFound(); var result = await _userManager.AddToRoleAsync(user, roleName); if (result.Succeeded) return Ok(); return BadRequest(result.Errors); } [HttpPost("ForgotPassword")] public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model) { try { if (string.IsNullOrEmpty(model.Email)) { return BadRequest("Email is required."); } var user = await _userManager.FindByEmailAsync(model.Email); if (user == null) { return Ok("User does not exist."); } var token = await _userManager.GeneratePasswordResetTokenAsync(user); var resetLink = Url.Action("ResetPassword", "User", new { token, email = user.Email }, protocol: HttpContext.Request.Scheme); await SendResetPasswordEmail(model.Email, resetLink); return Ok("Please check your email for password reset instructions."); } catch (Exception ex) { _logger.LogError(ex, "Error sending reset password email."); return StatusCode(StatusCodes.Status500InternalServerError, "Error sending reset password email."); } } [HttpPost("ResetPassword")] public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model) { if (!ModelState.IsValid) { return BadRequest("Invalid data."); } var user = await _userManager.FindByEmailAsync(model.Email); if (user == null) { return NotFound("User not found."); } var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password); if (result.Succeeded) { return Ok("Password has been reset successfully."); } return BadRequest("Error while resetting the password."); } private async Task SendResetPasswordEmail(string email, string resetLink) { try { var apiKey = _configuration["SendGrid:ApiKey"]; var client = new SendGridClient(apiKey); var from = new EmailAddress(_configuration["SendGrid:FromEmail"], _configuration["SendGrid:FromName"]); var to = new EmailAddress(email); var subject = "Reset Password"; var htmlContent = $"<h4>Reset your password by <a href='{resetLink}'>clicking here</a></h4>"; var msg = MailHelper.CreateSingleEmail(from, to, subject, null, htmlContent); var response = await client.SendEmailAsync(msg); if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Accepted) { throw new Exception($"Failed to send email. Status code: {response.StatusCode}"); } } catch (Exception ex) { _logger.LogError(ex, "Error sending reset password email."); throw; } } } }
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter