Preview:
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;
            }
        }
    }
}
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