feat: Add UserController and user-related DTOs for user management functionality
This commit is contained in:
483
Api/Controllers/UserController.cs
Normal file
483
Api/Controllers/UserController.cs
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Api.Models;
|
||||||
|
using Api.Models.DTOs;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Api.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class UserController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _context;
|
||||||
|
private readonly ILogger<UserController> _logger;
|
||||||
|
|
||||||
|
public UserController(AppDbContext context, ILogger<UserController> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all users with pagination support
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="page">Page number (default: 1)</param>
|
||||||
|
/// <param name="pageSize">Page size (default: 10, max: 100)</param>
|
||||||
|
/// <param name="search">Search term for email, first name, or last name</param>
|
||||||
|
/// <param name="isActive">Filter by active status</param>
|
||||||
|
/// <returns>Paginated list of users</returns>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<object>> GetUsers(
|
||||||
|
[FromQuery] int page = 1,
|
||||||
|
[FromQuery] int pageSize = 10,
|
||||||
|
[FromQuery] string? search = null,
|
||||||
|
[FromQuery] bool? isActive = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Validate pagination parameters
|
||||||
|
page = Math.Max(1, page);
|
||||||
|
pageSize = Math.Min(100, Math.Max(1, pageSize));
|
||||||
|
|
||||||
|
var query = _context.Users.Include(u => u.OAuthProviders).AsQueryable();
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
if (isActive.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(u => u.IsActive == isActive.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(search))
|
||||||
|
{
|
||||||
|
var searchTerm = search.ToLower();
|
||||||
|
query = query.Where(u =>
|
||||||
|
u.Email.ToLower().Contains(searchTerm) ||
|
||||||
|
(u.FirstName != null && u.FirstName.ToLower().Contains(searchTerm)) ||
|
||||||
|
(u.LastName != null && u.LastName.ToLower().Contains(searchTerm)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalCount = await query.CountAsync();
|
||||||
|
var users = await query
|
||||||
|
.OrderByDescending(u => u.CreatedAt)
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.Select(u => new UserDto
|
||||||
|
{
|
||||||
|
Id = u.Id,
|
||||||
|
Email = u.Email,
|
||||||
|
FirstName = u.FirstName,
|
||||||
|
LastName = u.LastName,
|
||||||
|
ProfilePictureUrl = u.ProfilePictureUrl,
|
||||||
|
CreatedAt = u.CreatedAt,
|
||||||
|
LastLoginAt = u.LastLoginAt,
|
||||||
|
IsActive = u.IsActive,
|
||||||
|
OAuthProviders = u.OAuthProviders.Select(op => new UserOAuthProviderDto
|
||||||
|
{
|
||||||
|
Id = op.Id,
|
||||||
|
Provider = op.Provider,
|
||||||
|
ProviderId = op.ProviderId,
|
||||||
|
ProviderEmail = op.ProviderEmail,
|
||||||
|
ProviderName = op.ProviderName,
|
||||||
|
CreatedAt = op.CreatedAt,
|
||||||
|
LastUsedAt = op.LastUsedAt
|
||||||
|
}).ToList()
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
users,
|
||||||
|
pagination = new
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
totalCount,
|
||||||
|
totalPages = (int)Math.Ceiling(totalCount / (double)pageSize),
|
||||||
|
hasNext = page * pageSize < totalCount,
|
||||||
|
hasPrevious = page > 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error retrieving users");
|
||||||
|
return StatusCode(500, "Internal server error while retrieving users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a specific user by ID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">User ID</param>
|
||||||
|
/// <returns>User details</returns>
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<UserDto>> GetUser(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = await _context.Users
|
||||||
|
.Include(u => u.OAuthProviders)
|
||||||
|
.FirstOrDefaultAsync(u => u.Id == id);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound($"User with ID {id} not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userDto = new UserDto
|
||||||
|
{
|
||||||
|
Id = user.Id,
|
||||||
|
Email = user.Email,
|
||||||
|
FirstName = user.FirstName,
|
||||||
|
LastName = user.LastName,
|
||||||
|
ProfilePictureUrl = user.ProfilePictureUrl,
|
||||||
|
CreatedAt = user.CreatedAt,
|
||||||
|
LastLoginAt = user.LastLoginAt,
|
||||||
|
IsActive = user.IsActive,
|
||||||
|
OAuthProviders = user.OAuthProviders.Select(op => new UserOAuthProviderDto
|
||||||
|
{
|
||||||
|
Id = op.Id,
|
||||||
|
Provider = op.Provider,
|
||||||
|
ProviderId = op.ProviderId,
|
||||||
|
ProviderEmail = op.ProviderEmail,
|
||||||
|
ProviderName = op.ProviderName,
|
||||||
|
CreatedAt = op.CreatedAt,
|
||||||
|
LastUsedAt = op.LastUsedAt
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(userDto);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error retrieving user with ID {UserId}", id);
|
||||||
|
return StatusCode(500, "Internal server error while retrieving user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current user's profile
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Current user's profile</returns>
|
||||||
|
[HttpGet("me")]
|
||||||
|
public async Task<ActionResult<UserDto>> GetCurrentUser()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userIdClaim = User.FindFirst("user_id")?.Value;
|
||||||
|
if (string.IsNullOrEmpty(userIdClaim) || !int.TryParse(userIdClaim, out var userId))
|
||||||
|
{
|
||||||
|
return Unauthorized("Invalid user token");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetUser(userId);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error retrieving current user profile");
|
||||||
|
return StatusCode(500, "Internal server error while retrieving current user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">User creation request</param>
|
||||||
|
/// <returns>Created user</returns>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<UserDto>> CreateUser([FromBody] CreateUserRequest request)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if user with this email already exists
|
||||||
|
var existingUser = await _context.Users
|
||||||
|
.FirstOrDefaultAsync(u => u.Email == request.Email);
|
||||||
|
|
||||||
|
if (existingUser != null)
|
||||||
|
{
|
||||||
|
return Conflict($"User with email {request.Email} already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = new User
|
||||||
|
{
|
||||||
|
Email = request.Email,
|
||||||
|
FirstName = request.FirstName,
|
||||||
|
LastName = request.LastName,
|
||||||
|
ProfilePictureUrl = request.ProfilePictureUrl,
|
||||||
|
IsActive = request.IsActive,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Users.Add(user);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Created new user with ID {UserId} and email {Email}", user.Id, user.Email);
|
||||||
|
|
||||||
|
var userDto = new UserDto
|
||||||
|
{
|
||||||
|
Id = user.Id,
|
||||||
|
Email = user.Email,
|
||||||
|
FirstName = user.FirstName,
|
||||||
|
LastName = user.LastName,
|
||||||
|
ProfilePictureUrl = user.ProfilePictureUrl,
|
||||||
|
CreatedAt = user.CreatedAt,
|
||||||
|
LastLoginAt = user.LastLoginAt,
|
||||||
|
IsActive = user.IsActive,
|
||||||
|
OAuthProviders = new List<UserOAuthProviderDto>()
|
||||||
|
};
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, userDto);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error creating user with email {Email}", request.Email);
|
||||||
|
return StatusCode(500, "Internal server error while creating user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update a user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">User ID</param>
|
||||||
|
/// <param name="request">User update request</param>
|
||||||
|
/// <returns>No content on success</returns>
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> UpdateUser(int id, [FromBody] UpdateUserRequest request)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = await _context.Users.FindAsync(id);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound($"User with ID {id} not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update only provided fields
|
||||||
|
if (request.FirstName != null)
|
||||||
|
{
|
||||||
|
user.FirstName = request.FirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.LastName != null)
|
||||||
|
{
|
||||||
|
user.LastName = request.LastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.ProfilePictureUrl != null)
|
||||||
|
{
|
||||||
|
user.ProfilePictureUrl = request.ProfilePictureUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.IsActive.HasValue)
|
||||||
|
{
|
||||||
|
user.IsActive = request.IsActive.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Entry(user).State = EntityState.Modified;
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Updated user with ID {UserId}", id);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
catch (DbUpdateConcurrencyException)
|
||||||
|
{
|
||||||
|
if (!await _context.Users.AnyAsync(e => e.Id == id))
|
||||||
|
{
|
||||||
|
return NotFound($"User with ID {id} not found");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error updating user with ID {UserId}", id);
|
||||||
|
return StatusCode(500, "Internal server error while updating user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the current user's profile
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">User update request</param>
|
||||||
|
/// <returns>No content on success</returns>
|
||||||
|
[HttpPut("me")]
|
||||||
|
public async Task<IActionResult> UpdateCurrentUser([FromBody] UpdateUserRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userIdClaim = User.FindFirst("user_id")?.Value;
|
||||||
|
if (string.IsNullOrEmpty(userIdClaim) || !int.TryParse(userIdClaim, out var userId))
|
||||||
|
{
|
||||||
|
return Unauthorized("Invalid user token");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await UpdateUser(userId, request);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error updating current user profile");
|
||||||
|
return StatusCode(500, "Internal server error while updating current user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete a user (soft delete by setting IsActive to false)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">User ID</param>
|
||||||
|
/// <returns>No content on success</returns>
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> DeleteUser(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = await _context.Users.FindAsync(id);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound($"User with ID {id} not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft delete by setting IsActive to false
|
||||||
|
user.IsActive = false;
|
||||||
|
_context.Entry(user).State = EntityState.Modified;
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Soft deleted user with ID {UserId}", id);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error deleting user with ID {UserId}", id);
|
||||||
|
return StatusCode(500, "Internal server error while deleting user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Permanently delete a user and all associated OAuth providers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">User ID</param>
|
||||||
|
/// <returns>No content on success</returns>
|
||||||
|
[HttpDelete("{id}/permanent")]
|
||||||
|
public async Task<IActionResult> PermanentlyDeleteUser(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = await _context.Users
|
||||||
|
.Include(u => u.OAuthProviders)
|
||||||
|
.FirstOrDefaultAsync(u => u.Id == id);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound($"User with ID {id} not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all OAuth providers first due to foreign key constraints
|
||||||
|
_context.UserOAuthProviders.RemoveRange(user.OAuthProviders);
|
||||||
|
|
||||||
|
// Remove the user
|
||||||
|
_context.Users.Remove(user);
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Permanently deleted user with ID {UserId} and {ProviderCount} OAuth providers",
|
||||||
|
id, user.OAuthProviders.Count);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error permanently deleting user with ID {UserId}", id);
|
||||||
|
return StatusCode(500, "Internal server error while permanently deleting user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reactivate a soft-deleted user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">User ID</param>
|
||||||
|
/// <returns>No content on success</returns>
|
||||||
|
[HttpPost("{id}/reactivate")]
|
||||||
|
public async Task<IActionResult> ReactivateUser(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = await _context.Users.FindAsync(id);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound($"User with ID {id} not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
user.IsActive = true;
|
||||||
|
_context.Entry(user).State = EntityState.Modified;
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Reactivated user with ID {UserId}", id);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error reactivating user with ID {UserId}", id);
|
||||||
|
return StatusCode(500, "Internal server error while reactivating user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get user statistics
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>User statistics</returns>
|
||||||
|
[HttpGet("statistics")]
|
||||||
|
public async Task<ActionResult<object>> GetUserStatistics()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var totalUsers = await _context.Users.CountAsync();
|
||||||
|
var activeUsers = await _context.Users.CountAsync(u => u.IsActive);
|
||||||
|
var inactiveUsers = totalUsers - activeUsers;
|
||||||
|
var usersWithLogin = await _context.Users.CountAsync(u => u.LastLoginAt != null);
|
||||||
|
var recentUsers = await _context.Users.CountAsync(u => u.CreatedAt >= DateTime.UtcNow.AddDays(-30));
|
||||||
|
|
||||||
|
var providerStats = await _context.UserOAuthProviders
|
||||||
|
.GroupBy(op => op.Provider)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
Provider = g.Key.ToString(),
|
||||||
|
Count = g.Count(),
|
||||||
|
UniqueUsers = g.Select(op => op.UserId).Distinct().Count()
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
totalUsers,
|
||||||
|
activeUsers,
|
||||||
|
inactiveUsers,
|
||||||
|
usersWithLogin,
|
||||||
|
recentUsers,
|
||||||
|
providerStatistics = providerStats
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error retrieving user statistics");
|
||||||
|
return StatusCode(500, "Internal server error while retrieving statistics");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,64 @@ namespace Api.Models.DTOs
|
|||||||
public List<string> Providers { get; set; } = new List<string>();
|
public List<string> Providers { get; set; } = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User CRUD DTOs
|
||||||
|
public class CreateUserRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
[StringLength(255)]
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[StringLength(255)]
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
|
||||||
|
[StringLength(255)]
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
|
||||||
|
[StringLength(500)]
|
||||||
|
public string? ProfilePictureUrl { get; set; }
|
||||||
|
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateUserRequest
|
||||||
|
{
|
||||||
|
[StringLength(255)]
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
|
||||||
|
[StringLength(255)]
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
|
||||||
|
[StringLength(500)]
|
||||||
|
public string? ProfilePictureUrl { get; set; }
|
||||||
|
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
public string? ProfilePictureUrl { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime? LastLoginAt { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public List<UserOAuthProviderDto> OAuthProviders { get; set; } = new List<UserOAuthProviderDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserOAuthProviderDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public OAuthProvider Provider { get; set; }
|
||||||
|
public string ProviderId { get; set; } = string.Empty;
|
||||||
|
public string? ProviderEmail { get; set; }
|
||||||
|
public string? ProviderName { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime? LastUsedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class JwtSettings
|
public class JwtSettings
|
||||||
{
|
{
|
||||||
public string SecretKey { get; set; } = string.Empty;
|
public string SecretKey { get; set; } = string.Empty;
|
||||||
|
|||||||
70
Api/User.http
Normal file
70
Api/User.http
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
### User Controller API Tests
|
||||||
|
|
||||||
|
@baseUrl = http://localhost:5000
|
||||||
|
@authToken = YOUR_JWT_TOKEN_HERE
|
||||||
|
|
||||||
|
### Get all users with pagination
|
||||||
|
GET {{baseUrl}}/api/user?page=1&pageSize=10
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
|
||||||
|
### Get all users with search
|
||||||
|
GET {{baseUrl}}/api/user?search=john&isActive=true
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
|
||||||
|
### Get specific user by ID
|
||||||
|
GET {{baseUrl}}/api/user/1
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
|
||||||
|
### Get current user profile
|
||||||
|
GET {{baseUrl}}/api/user/me
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
|
||||||
|
### Create a new user
|
||||||
|
POST {{baseUrl}}/api/user
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "newuser@example.com",
|
||||||
|
"firstName": "John",
|
||||||
|
"lastName": "Doe",
|
||||||
|
"profilePictureUrl": "https://example.com/profile.jpg",
|
||||||
|
"isActive": true
|
||||||
|
}
|
||||||
|
|
||||||
|
### Update a user
|
||||||
|
PUT {{baseUrl}}/api/user/1
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"firstName": "Updated John",
|
||||||
|
"lastName": "Updated Doe",
|
||||||
|
"isActive": true
|
||||||
|
}
|
||||||
|
|
||||||
|
### Update current user profile
|
||||||
|
PUT {{baseUrl}}/api/user/me
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"firstName": "My Updated Name",
|
||||||
|
"profilePictureUrl": "https://example.com/new-profile.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
### Soft delete user (deactivate)
|
||||||
|
DELETE {{baseUrl}}/api/user/1
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
|
||||||
|
### Reactivate a soft-deleted user
|
||||||
|
POST {{baseUrl}}/api/user/1/reactivate
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
|
||||||
|
### Permanently delete user
|
||||||
|
DELETE {{baseUrl}}/api/user/1/permanent
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
|
|
||||||
|
### Get user statistics
|
||||||
|
GET {{baseUrl}}/api/user/statistics
|
||||||
|
Authorization: Bearer {{authToken}}
|
||||||
@@ -305,7 +305,7 @@ export class AuthenticationService {
|
|||||||
redirectUri: `${window.location.origin}/authentication/callback`
|
redirectUri: `${window.location.origin}/authentication/callback`
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Google OAuth Config:', googleConfig);
|
console.log('Google OAuth Config:', JSON.stringify(googleConfig));
|
||||||
return this.startLogin(googleConfig);
|
return this.startLogin(googleConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user