You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
17 KiB
C#
466 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Haoliang.Core.Services;
|
|
using Haoliang.Models.User;
|
|
using Haoliang.Models.Common;
|
|
|
|
namespace Haoliang.Api.Controllers
|
|
{
|
|
[ApiController]
|
|
[Route("api/v1/[controller]")]
|
|
public class AuthController : ControllerBase
|
|
{
|
|
private readonly IAuthService _authService;
|
|
private readonly IUserService _userService;
|
|
private readonly IPermissionService _permissionService;
|
|
private readonly ILoggingService _loggingService;
|
|
|
|
public AuthController(
|
|
IAuthService authService,
|
|
IUserService userService,
|
|
IPermissionService permissionService,
|
|
ILoggingService loggingService)
|
|
{
|
|
_authService = authService;
|
|
_userService = userService;
|
|
_permissionService = permissionService;
|
|
_loggingService = loggingService;
|
|
}
|
|
|
|
[HttpPost("login")]
|
|
public async Task<IActionResult> Login([FromBody] LoginRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return BadRequest(ApiResponse<AuthResult>.Error("Invalid request data", 400));
|
|
}
|
|
|
|
var result = await _authService.LoginAsync(request);
|
|
|
|
if (result.Success)
|
|
{
|
|
await _loggingService.LogInformationAsync($"User {request.Username} logged in successfully");
|
|
return Ok(ApiResponse<AuthResult>.Ok(result));
|
|
}
|
|
else
|
|
{
|
|
await _loggingService.LogWarningAsync($"Failed login attempt for user {request.Username}");
|
|
return Unauthorized(ApiResponse<AuthResult>.Error("Invalid username or password", 401));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Login error for user {request.Username}: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<AuthResult>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpPost("logout")]
|
|
public async Task<IActionResult> Logout([FromBody] LogoutRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (request?.UserId == null || request.UserId == 0)
|
|
{
|
|
return BadRequest(ApiResponse<bool>.Error("User ID is required", 400));
|
|
}
|
|
|
|
var success = await _authService.LogoutAsync(request.UserId);
|
|
|
|
if (success)
|
|
{
|
|
await _loggingService.LogInformationAsync($"User {request.UserId} logged out successfully");
|
|
return Ok(ApiResponse<bool>.Ok(true));
|
|
}
|
|
else
|
|
{
|
|
return BadRequest(ApiResponse<bool>.Error("Logout failed", 400));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Logout error: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<bool>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpPost("refresh")]
|
|
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return BadRequest(ApiResponse<AuthResult>.Error("Invalid request data", 400));
|
|
}
|
|
|
|
var result = await _authService.RefreshTokenAsync(request.RefreshToken);
|
|
|
|
if (result.Success)
|
|
{
|
|
return Ok(ApiResponse<AuthResult>.Ok(result));
|
|
}
|
|
else
|
|
{
|
|
return Unauthorized(ApiResponse<AuthResult>.Error("Invalid refresh token", 401));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Token refresh error: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<AuthResult>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpPost("register")]
|
|
public async Task<IActionResult> Register([FromBody] RegisterRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return BadRequest(ApiResponse<UserViewModel>.Error("Invalid request data", 400));
|
|
}
|
|
|
|
// Check if username already exists
|
|
var usernameExists = await _authService.UsernameExistsAsync(request.Username);
|
|
if (usernameExists)
|
|
{
|
|
return BadRequest(ApiResponse<UserViewModel>.Error("Username already exists", 400));
|
|
}
|
|
|
|
// Check if email already exists
|
|
var emailExists = await _authService.EmailExistsAsync(request.Email);
|
|
if (emailExists)
|
|
{
|
|
return BadRequest(ApiResponse<UserViewModel>.Error("Email already exists", 400));
|
|
}
|
|
|
|
// Create user
|
|
var user = new User
|
|
{
|
|
Username = request.Username,
|
|
Email = request.Email,
|
|
PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password),
|
|
FirstName = request.FirstName,
|
|
LastName = request.LastName,
|
|
Department = request.Department,
|
|
IsActive = true,
|
|
CreatedAt = DateTime.Now,
|
|
UpdatedAt = DateTime.Now
|
|
};
|
|
|
|
var userViewModel = await _userService.CreateUserAsync(user);
|
|
|
|
await _loggingService.LogInformationAsync($"New user registered: {request.Username}");
|
|
return Ok(ApiResponse<UserViewModel>.Ok(userViewModel));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Registration error for user {request.Username}: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<UserViewModel>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpGet("profile")]
|
|
public async Task<IActionResult> GetProfile()
|
|
{
|
|
try
|
|
{
|
|
// Get user ID from claims (in real implementation, use JWT token validation)
|
|
var userId = GetCurrentUserId();
|
|
if (userId == null)
|
|
{
|
|
return Unauthorized(ApiResponse<UserViewModel>.Error("Not authenticated", 401));
|
|
}
|
|
|
|
var user = await _userService.GetUserByIdAsync(userId.Value);
|
|
if (user == null)
|
|
{
|
|
return NotFound(ApiResponse<UserViewModel>.Error("User not found", 404));
|
|
}
|
|
|
|
return Ok(ApiResponse<UserViewModel>.Ok(user));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Profile fetch error: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<UserViewModel>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpPut("profile")]
|
|
public async Task<IActionResult> UpdateProfile([FromBody] UpdateUserProfileRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return BadRequest(ApiResponse<UserViewModel>.Error("Invalid request data", 400));
|
|
}
|
|
|
|
var userId = GetCurrentUserId();
|
|
if (userId == null)
|
|
{
|
|
return Unauthorized(ApiResponse<UserViewModel>.Error("Not authenticated", 401));
|
|
}
|
|
|
|
var user = new User
|
|
{
|
|
Username = request.Username,
|
|
Email = request.Email,
|
|
FirstName = request.FirstName,
|
|
LastName = request.LastName,
|
|
Department = request.Department,
|
|
UpdatedAt = DateTime.Now
|
|
};
|
|
|
|
var updatedUser = await _userService.UpdateUserAsync(userId.Value, user);
|
|
|
|
await _loggingService.LogInformationAsync($"User profile updated: {request.Username}");
|
|
return Ok(ApiResponse<UserViewModel>.Ok(updatedUser));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Profile update error: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<UserViewModel>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpPost("change-password")]
|
|
public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return BadRequest(ApiResponse<bool>.Error("Invalid request data", 400));
|
|
}
|
|
|
|
var userId = GetCurrentUserId();
|
|
if (userId == null)
|
|
{
|
|
return Unauthorized(ApiResponse<bool>.Error("Not authenticated", 401));
|
|
}
|
|
|
|
var success = await _userService.ChangePasswordAsync(
|
|
userId.Value,
|
|
request.OldPassword,
|
|
request.NewPassword);
|
|
|
|
if (success)
|
|
{
|
|
await _loggingService.LogInformationAsync($"Password changed for user: {userId}");
|
|
return Ok(ApiResponse<bool>.Ok(true));
|
|
}
|
|
else
|
|
{
|
|
return BadRequest(ApiResponse<bool>.Error("Invalid old password", 400));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Password change error: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<bool>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpGet("permissions")]
|
|
public async Task<IActionResult> GetUserPermissions()
|
|
{
|
|
try
|
|
{
|
|
var userId = GetCurrentUserId();
|
|
if (userId == null)
|
|
{
|
|
return Unauthorized(ApiResponse<IEnumerable<string>>.Error("Not authenticated", 401));
|
|
}
|
|
|
|
var permissions = await _permissionService.GetUserPermissionsAsync(userId.Value);
|
|
return Ok(ApiResponse<IEnumerable<string>>.Ok(permissions));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Permission fetch error: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<IEnumerable<string>>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpGet("users")]
|
|
[ProducesResponseType(typeof(PaginatedResponse<UserViewModel>), 200)]
|
|
public async Task<IActionResult> GetAllUsers([FromQuery] UserFilter filter)
|
|
{
|
|
try
|
|
{
|
|
var users = await _userService.GetAllUsersAsync();
|
|
|
|
// Apply filters
|
|
if (!string.IsNullOrEmpty(filter.Role))
|
|
{
|
|
users = users.Where(u => u.Role == filter.Role);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(filter.Department))
|
|
{
|
|
users = users.Where(u => u.Department == filter.Department);
|
|
}
|
|
|
|
if (filter.IsActive.HasValue)
|
|
{
|
|
users = users.Where(u => u.IsActive == filter.IsActive.Value);
|
|
}
|
|
|
|
var totalCount = users.Count();
|
|
|
|
// Apply pagination
|
|
var pagedUsers = users
|
|
.Skip((filter.PageNumber - 1) * filter.PageSize)
|
|
.Take(filter.PageSize)
|
|
.ToList();
|
|
|
|
var response = new PaginatedResponse<UserViewModel>
|
|
{
|
|
Items = pagedUsers,
|
|
TotalCount = totalCount,
|
|
PageNumber = filter.PageNumber,
|
|
PageSize = filter.PageSize,
|
|
TotalPages = (int)Math.Ceiling((double)totalCount / filter.PageSize),
|
|
HasPreviousPage = filter.PageNumber > 1,
|
|
HasNextPage = filter.PageNumber < (int)Math.Ceiling((double)totalCount / filter.PageSize)
|
|
};
|
|
|
|
return Ok(response);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"Users fetch error: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<PaginatedResponse<UserViewModel>>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpGet("users/{id}")]
|
|
public async Task<IActionResult> GetUserById(int id)
|
|
{
|
|
try
|
|
{
|
|
var user = await _userService.GetUserByIdAsync(id);
|
|
if (user == null)
|
|
{
|
|
return NotFound(ApiResponse<UserViewModel>.Error("User not found", 404));
|
|
}
|
|
|
|
return Ok(ApiResponse<UserViewModel>.Ok(user));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"User fetch error for ID {id}: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<UserViewModel>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpPost("users/{id}/activate")]
|
|
public async Task<IActionResult> ActivateUser(int id)
|
|
{
|
|
try
|
|
{
|
|
var success = await _userService.ActivateUserAsync(id);
|
|
if (success)
|
|
{
|
|
await _loggingService.LogInformationAsync($"User activated: {id}");
|
|
return Ok(ApiResponse<bool>.Ok(true));
|
|
}
|
|
else
|
|
{
|
|
return BadRequest(ApiResponse<bool>.Error("Failed to activate user", 400));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"User activation error for ID {id}: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<bool>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
[HttpPost("users/{id}/deactivate")]
|
|
public async Task<IActionResult> DeactivateUser(int id)
|
|
{
|
|
try
|
|
{
|
|
var success = await _userService.DeactivateUserAsync(id);
|
|
if (success)
|
|
{
|
|
await _loggingService.LogInformationAsync($"User deactivated: {id}");
|
|
return Ok(ApiResponse<bool>.Ok(true));
|
|
}
|
|
else
|
|
{
|
|
return BadRequest(ApiResponse<bool>.Error("Failed to deactivate user", 400));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _loggingService.LogErrorAsync($"User deactivation error for ID {id}: {ex.Message}", ex);
|
|
return StatusCode(500, ApiResponse<bool>.Error("Internal server error", 500));
|
|
}
|
|
}
|
|
|
|
private int? GetCurrentUserId()
|
|
{
|
|
// In a real implementation, extract user ID from JWT token claims
|
|
// For now, return null (would be implemented in JWT middleware)
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Supporting request and response models
|
|
public class LogoutRequest
|
|
{
|
|
public int UserId { get; set; }
|
|
}
|
|
|
|
public class RefreshTokenRequest
|
|
{
|
|
public string RefreshToken { get; set; }
|
|
}
|
|
|
|
public class RegisterRequest
|
|
{
|
|
public string Username { get; set; }
|
|
public string Email { get; set; }
|
|
public string Password { get; set; }
|
|
public string ConfirmPassword { get; set; }
|
|
public string FirstName { get; set; }
|
|
public string LastName { get; set; }
|
|
public string Department { get; set; }
|
|
}
|
|
|
|
public class UpdateUserProfileRequest
|
|
{
|
|
public string Username { get; set; }
|
|
public string Email { get; set; }
|
|
public string FirstName { get; set; }
|
|
public string LastName { get; set; }
|
|
public string Department { get; set; }
|
|
}
|
|
|
|
public class ChangePasswordRequest
|
|
{
|
|
public string OldPassword { get; set; }
|
|
public string NewPassword { get; set; }
|
|
public string ConfirmPassword { get; set; }
|
|
}
|
|
|
|
public class UserFilter
|
|
{
|
|
public int PageNumber { get; set; } = 1;
|
|
public int PageSize { get; set; } = 10;
|
|
public string Role { get; set; }
|
|
public string Department { get; set; }
|
|
public bool? IsActive { get; set; }
|
|
}
|
|
} |