236 lines
9.0 KiB
C#
236 lines
9.0 KiB
C#
using Butter.Dtos;
|
|
using Butter.Dtos.Person;
|
|
using Butter.Types;
|
|
using Lactose.Mapper;
|
|
using Lactose.Repositories;
|
|
using Lactose.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Lactose.Controllers;
|
|
|
|
/// <summary>
|
|
/// Manages people (faces/individuals identified in assets).
|
|
/// </summary>
|
|
[ApiController]
|
|
[Authorize]
|
|
[Route("api/[controller]")]
|
|
public class PersonController(
|
|
IPersonRepository personRepository,
|
|
IAlbumRepository albumRepository,
|
|
LactoseAuthService authService
|
|
) : ControllerBase {
|
|
/// <summary>
|
|
/// Gets a person by ID with their associated albums, filtered by access level.
|
|
/// </summary>
|
|
/// <summary>
|
|
/// Gets a person by ID with their associated albums, filtered by access level and paginated.
|
|
/// </summary>
|
|
/// <param name="id">The person ID.</param>
|
|
/// <param name="albumPaging">Pagination, search, and sort parameters for albums.</param>
|
|
/// <returns>The person details with albums, or 404 if not found.</returns>
|
|
[HttpGet("{id}")]
|
|
public ActionResult<PersonDetailedDto> Get([FromRoute] Guid id, [FromQuery] PagedSearchParametersDto albumPaging) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
var person = personRepository.Find(id);
|
|
if (person == null) return NotFound();
|
|
|
|
if (person.Albums != null && accessLevel == EAccessLevel.User) {
|
|
var owned = person.Albums.Where(a => a.UserOwnerId == uid).ToList();
|
|
var publicShared = person.Albums
|
|
.Where(a => a.Assets != null && a.Assets.Any(asset => asset is { IsPubliclyShared: true, DeletedAt: null }))
|
|
.ToList();
|
|
var shared = person.Albums
|
|
.Where(a => a.Assets != null && a.Assets.Any(asset => asset is {
|
|
DeletedAt: null, SharedWith: { Count: > 0 }
|
|
} && asset.SharedWith!.Any(u => u.Id == uid)))
|
|
.ToList();
|
|
|
|
person.Albums = owned
|
|
.Union(publicShared)
|
|
.Union(shared)
|
|
.DistinctBy(a => a.Id)
|
|
.ToList();
|
|
}
|
|
|
|
if (person.Albums != null) {
|
|
if (!string.IsNullOrEmpty(albumPaging.Search))
|
|
{
|
|
person.Albums = person.Albums
|
|
.Where(a => a.Title.Contains(albumPaging.Search, StringComparison.OrdinalIgnoreCase))
|
|
.ToList();
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(albumPaging.SortBy))
|
|
{
|
|
person.Albums = albumPaging.SortBy.ToLowerInvariant() switch
|
|
{
|
|
"name" => albumPaging.SortAsc
|
|
? [..person.Albums.OrderBy(a => a.Title)]
|
|
: [..person.Albums.OrderByDescending(a => a.Title)],
|
|
"created" => albumPaging.SortAsc
|
|
? [..person.Albums.OrderBy(a => a.CreatedAt)]
|
|
: [..person.Albums.OrderByDescending(a => a.CreatedAt)],
|
|
"updated" => albumPaging.SortAsc
|
|
? [..person.Albums.OrderBy(a => a.UpdatedAt)]
|
|
: [..person.Albums.OrderByDescending(a => a.UpdatedAt)],
|
|
"assets" => albumPaging.SortAsc
|
|
? [..person.Albums.OrderBy(a => a.Assets?.Count(asset => asset.DeletedAt == null) ?? 0)]
|
|
: [..person.Albums.OrderByDescending(a => a.Assets?.Count(asset => asset.DeletedAt == null) ?? 0)],
|
|
_ => person.Albums
|
|
};
|
|
}
|
|
|
|
person.Albums = person.Albums
|
|
.Skip(albumPaging.Page * albumPaging.PageSize)
|
|
.Take(albumPaging.PageSize)
|
|
.ToList();
|
|
}
|
|
|
|
return Ok(person.ToPersonDetailedDto());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches people with pagination, optional search term, sort options, filtering out cosplayers with no visible content for non-admin users.
|
|
/// </summary>
|
|
/// <param name="pagedSearch">Pagination, search, and sort parameters.</param>
|
|
/// <returns>A paginated list of person previews.</returns>
|
|
[HttpGet]
|
|
public ActionResult<List<PersonPreviewDto>> GetAll([FromQuery] PersonSearchParametersDto pagedSearch) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
if (pagedSearch.Page < 0 || pagedSearch.PageSize < 1 || pagedSearch.PageSize > PagedParametersDto.MaxPageSize) { return BadRequest(); }
|
|
|
|
var people = personRepository.SearchQuery(
|
|
pagedSearch.Search ?? string.Empty,
|
|
pagedSearch.Page,
|
|
pagedSearch.PageSize,
|
|
pagedSearch.SortBy,
|
|
pagedSearch.SortAsc,
|
|
uid ?? default,
|
|
accessLevel
|
|
).ToList();
|
|
return Ok(people);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates a person.
|
|
/// </summary>
|
|
/// <param name="id">The person ID.</param>
|
|
/// <param name="dto">The updated person data.</param>
|
|
/// <returns>200 on success.</returns>
|
|
[HttpPost("{id}")]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public IActionResult Update([FromRoute] Guid id, [FromBody] PersonUpdateDto dto) {
|
|
var person = personRepository.Find(id);
|
|
if (person == null) return NotFound();
|
|
|
|
person.Name = dto.Name;
|
|
person.ProfileAssetId = dto.ProfileAssetId;
|
|
person.ProfileCropX = dto.ProfileCropX;
|
|
person.ProfileCropY = dto.ProfileCropY;
|
|
person.ProfileCropZoom = dto.ProfileCropZoom;
|
|
person.UpdatedAt = DateTime.UtcNow;
|
|
|
|
personRepository.Save();
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates multiple people in a bulk operation.
|
|
/// </summary>
|
|
/// <param name="personBulkDto">The bulk update data.</param>
|
|
/// <returns>200 on success.</returns>
|
|
[HttpPost]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public IActionResult BulkUpdate([FromBody] BulkDto<PersonUpdateDto> personBulkDto) {
|
|
var people = personRepository.FindBulk(personBulkDto.Ids).ToList();
|
|
|
|
foreach (var person in people) {
|
|
person.Name = personBulkDto.Data.Name;
|
|
person.ProfileAssetId = personBulkDto.Data.ProfileAssetId;
|
|
person.ProfileCropX = personBulkDto.Data.ProfileCropX;
|
|
person.ProfileCropY = personBulkDto.Data.ProfileCropY;
|
|
person.ProfileCropZoom = personBulkDto.Data.ProfileCropZoom;
|
|
person.UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
personRepository.Save();
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a person by ID (soft delete).
|
|
/// </summary>
|
|
/// <param name="id">The person ID.</param>
|
|
/// <returns>200 on success, 404 if not found.</returns>
|
|
/// <summary>
|
|
/// Deletes a person by ID (hard delete). Cascades to unlink all associated albums.
|
|
/// </summary>
|
|
/// <returns>200 on success, 404 if not found.</returns>
|
|
[HttpDelete("{id}")]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public IActionResult Delete([FromRoute] Guid id) {
|
|
var person = personRepository.Find(id);
|
|
if (person == null) return NotFound();
|
|
|
|
// Unlink all albums owned by this person
|
|
var albums = albumRepository.FindByPerson(id).ToList();
|
|
foreach (var album in albums) {
|
|
album.PersonOwnerId = null;
|
|
}
|
|
|
|
personRepository.Remove(person);
|
|
personRepository.Save();
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes multiple people in a bulk operation.
|
|
/// </summary>
|
|
/// <param name="ids">The list of person IDs to delete.</param>
|
|
/// <returns>200 on success.</returns>
|
|
/// <summary>
|
|
/// Deletes multiple people in a bulk operation (hard delete). Cascades to unlink all associated albums.
|
|
/// </summary>
|
|
/// <returns>200 on success.</returns>
|
|
[HttpDelete]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public IActionResult BulkDelete([FromBody] List<Guid> ids) {
|
|
var people = personRepository.FindBulk(ids).ToList();
|
|
|
|
foreach (var person in people) {
|
|
// Unlink all albums owned by this person
|
|
var albums = albumRepository.FindByPerson(person.Id).ToList();
|
|
foreach (var album in albums) {
|
|
album.PersonOwnerId = null;
|
|
}
|
|
personRepository.Remove(person);
|
|
}
|
|
|
|
personRepository.Save();
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new person.
|
|
/// </summary>
|
|
/// <param name="dto">The person creation data.</param>
|
|
/// <returns>200 with the new person ID.</returns>
|
|
[HttpPut]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public ActionResult<Guid> Create([FromBody] PersonCreateDto dto) {
|
|
var person = new Models.Person {
|
|
Id = Guid.NewGuid(),
|
|
Name = dto.Name,
|
|
ProfileAssetId = dto.ProfileAssetId,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
personRepository.Insert(person);
|
|
personRepository.Save();
|
|
return Ok(person.Id);
|
|
}
|
|
} |