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.Api.Services; using Moonlight.Shared; using Moonlight.Shared.Http.Requests; using Moonlight.Shared.Http.Requests.Admin.Themes; using Moonlight.Shared.Http.Responses; using Moonlight.Shared.Http.Responses.Admin.Themes; namespace Moonlight.Api.Http.Controllers.Admin; [ApiController] [Route("api/admin/themes")] public class ThemesController : Controller { private readonly DatabaseRepository ThemeRepository; private readonly FrontendService FrontendService; public ThemesController(DatabaseRepository themeRepository, FrontendService frontendService) { ThemeRepository = themeRepository; FrontendService = frontendService; } [HttpGet] [Authorize(Policy = Permissions.Themes.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 = ThemeRepository .Query(); // Filters if (filterOptions != null) { foreach (var filterOption in filterOptions.Filters) { query = filterOption.Key switch { nameof(Theme.Name) => query.Where(user => EF.Functions.ILike(user.Name, $"%{filterOption.Value}%")), nameof(Theme.Version) => query.Where(user => EF.Functions.ILike(user.Version, $"%{filterOption.Value}%")), nameof(Theme.Author) => query.Where(user => EF.Functions.ILike(user.Author, $"%{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.Themes.View)] public async Task> GetAsync([FromRoute] int id) { var item = await ThemeRepository .Query() .FirstOrDefaultAsync(x => x.Id == id); if (item == null) return Problem("No theme with this id", statusCode: 404); return ThemeMapper.ToDto(item); } [HttpPost] [Authorize(Policy = Permissions.Themes.Create)] public async Task> CreateAsync([FromBody] CreateThemeDto request) { var theme = ThemeMapper.ToEntity(request); var finalTheme = await ThemeRepository.AddAsync(theme); await FrontendService.ResetCacheAsync(); return ThemeMapper.ToDto(finalTheme); } [HttpPatch("{id:int}")] [Authorize(Policy = Permissions.Themes.Edit)] public async Task> UpdateAsync([FromRoute] int id, [FromBody] UpdateThemeDto request) { var theme = await ThemeRepository .Query() .FirstOrDefaultAsync(x => x.Id == id); if (theme == null) return Problem("No theme with this id found", statusCode: 404); ThemeMapper.Merge(theme, request); await ThemeRepository.UpdateAsync(theme); await FrontendService.ResetCacheAsync(); return ThemeMapper.ToDto(theme); } [HttpDelete("{id:int}")] [Authorize(Policy = Permissions.Themes.Delete)] public async Task DeleteAsync([FromRoute] int id) { var theme = await ThemeRepository .Query() .FirstOrDefaultAsync(x => x.Id == id); if (theme == null) return Problem("No theme with this id found", statusCode: 404); await ThemeRepository.RemoveAsync(theme); await FrontendService.ResetCacheAsync(); return NoContent(); } }