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