using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Moonlight.Api.Database; using Moonlight.Api.Database.Entities; using Moonlight.Api.Mappers; using Moonlight.Shared; using Moonlight.Shared.Http.Requests; using Moonlight.Shared.Http.Requests.Admin.Users; using Moonlight.Shared.Http.Responses; using Moonlight.Shared.Http.Responses.Admin.Users; namespace Moonlight.Api.Http.Controllers.Admin; [Authorize] [ApiController] [Route("api/admin/users")] public class UsersController : Controller { private readonly DatabaseRepository UserRepository; public UsersController(DatabaseRepository userRepository) { UserRepository = userRepository; } [HttpGet] [Authorize(Policy = Permissions.Users.View)] public async Task>> GetAsync( [FromQuery] int startIndex, [FromQuery] int length, [FromQuery] FilterOptions? filterOptions ) { // Validation if (startIndex < 0) return Problem("Invalid start index specified", statusCode: 400); if (length is < 1 or > 100) return Problem("Invalid length specified"); // Query building var query = UserRepository .Query(); // Filters if (filterOptions != null) { foreach (var filterOption in filterOptions.Filters) { query = filterOption.Key switch { nameof(Database.Entities.User.Email) => query.Where(user => EF.Functions.ILike(user.Email, $"%{filterOption.Value}%")), nameof(Database.Entities.User.Username) => query.Where(user => EF.Functions.ILike(user.Username, $"%{filterOption.Value}%")), _ => query }; } } // Pagination var data = await query .OrderBy(x => x.Id) .ProjectToDto() .Skip(startIndex) .Take(length) .ToArrayAsync(); var total = await query.CountAsync(); return new PagedData(data, total); } [HttpGet("{id:int}")] [Authorize(Policy = Permissions.Users.View)] public async Task> GetAsync([FromRoute] int id) { var user = await UserRepository .Query() .FirstOrDefaultAsync(x => x.Id == id); if (user == null) return Problem("No user with this id found", statusCode: 404); return UserMapper.ToDto(user); } [HttpPost] [Authorize(Policy = Permissions.Users.Create)] public async Task> CreateAsync([FromBody] CreateUserDto request) { var user = UserMapper.ToEntity(request); user.InvalidateTimestamp = DateTimeOffset.UtcNow.AddMinutes(-1); var finalUser = await UserRepository.AddAsync(user); return UserMapper.ToDto(finalUser); } [HttpPatch("{id:int}")] [Authorize(Policy = Permissions.Users.Edit)] public async Task> UpdateAsync([FromRoute] int id, [FromBody] UpdateUserDto request) { var user = await UserRepository .Query() .FirstOrDefaultAsync(x => x.Id == id); if (user == null) return Problem("No user with this id found", statusCode: 404); UserMapper.Merge(user, request); await UserRepository.UpdateAsync(user); return UserMapper.ToDto(user); } [HttpDelete("{id:int}")] [Authorize(Policy = Permissions.Users.Delete)] public async Task DeleteAsync([FromRoute] int id) { var user = await UserRepository .Query() .FirstOrDefaultAsync(user => user.Id == id); if (user == null) return Problem("No user with this id found", statusCode: 404); await UserRepository.RemoveAsync(user); return NoContent(); } }