279 lines
11 KiB
C#
279 lines
11 KiB
C#
using Butter.Dtos;
|
|
using Butter.Dtos.Album;
|
|
using Butter.Types;
|
|
using Lactose.Mapper;
|
|
using Lactose.Models;
|
|
using Lactose.Repositories;
|
|
using Lactose.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Lactose.Controllers;
|
|
|
|
/// <summary>
|
|
/// Manages albums — collections of assets.
|
|
/// </summary>
|
|
/// <param name="authService">Authentication service.</param>
|
|
/// <param name="albumRepository">Album repository.</param>
|
|
/// <param name="assetRepository">Asset repository.</param>
|
|
[ApiController]
|
|
[Authorize]
|
|
[Route("api/[controller]")]
|
|
public class AlbumController(
|
|
//TODO: Add logging
|
|
//ILogger<AlbumController> logger,
|
|
LactoseAuthService authService,
|
|
IAlbumRepository albumRepository,
|
|
IAssetRepository assetRepository
|
|
) : ControllerBase {
|
|
/// <summary>
|
|
/// Gets an album by its ID with filtered assets based on access level.
|
|
/// </summary>
|
|
/// <param name="id">The album ID.</param>
|
|
/// <returns>The full album details with accessible assets, or 404 if not found.</returns>
|
|
[HttpGet("{id}")]
|
|
public ActionResult<AlbumFullDto> Get([FromRoute] Guid id) {
|
|
Guid? uid = authService.GetUserData(User)?.Id;
|
|
EAccessLevel accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
var album = albumRepository.Find(id);
|
|
|
|
// If the album does not exist, return a 404
|
|
if (album == null) return NotFound();
|
|
|
|
// If the album has no assets, return an empty list
|
|
if (album.Assets == null) return Ok(album.ToAlbumFullDto());
|
|
|
|
//Storage for assets that will be sent out;
|
|
List<Asset> assets = new();
|
|
|
|
switch (accessLevel) {
|
|
case EAccessLevel.User:
|
|
// Only publicly shared assets or owned by user
|
|
assets.AddRange(album.Assets.Where(asset => (asset.IsPubliclyShared || asset.OwnerId == uid) && asset.DeletedAt == null));
|
|
// Add shared assets
|
|
assets.AddRange(album.Assets.Where(asset => asset.SharedWith!.Any(user => user.Id == uid) && asset.DeletedAt == null));
|
|
break;
|
|
case EAccessLevel.Curator:
|
|
// all assets minus deleted
|
|
assets.AddRange(album.Assets.Where(asset => asset.DeletedAt == null));
|
|
break;
|
|
case EAccessLevel.Admin:
|
|
// All assets
|
|
assets.AddRange(album.Assets);
|
|
break;
|
|
}
|
|
|
|
// changes the retrieved album to the new filtered
|
|
album.Assets = assets.OrderBy(a => a.OriginalFilename).ToList();
|
|
return Ok(album.ToAlbumFullDto());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches albums with pagination and optional search term.
|
|
/// </summary>
|
|
/// <param name="pagingOptions">Pagination and search parameters.</param>
|
|
/// <returns>A list of album previews accessible to the user.</returns>
|
|
[HttpGet]
|
|
public ActionResult<List<AlbumPreviewDto>> Search([FromQuery] PagedSearchParametersDto pagingOptions) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
if(pagingOptions.Page < 0 || pagingOptions.PageSize < 0) return BadRequest();
|
|
|
|
var albums = albumRepository.SearchQuery(pagingOptions.Search ?? string.Empty, pagingOptions.Page, pagingOptions.PageSize).ToList();
|
|
|
|
switch (accessLevel) {
|
|
default:
|
|
case EAccessLevel.User:
|
|
List<Album> filteredAlbums;
|
|
// Only publicly shared albums or owned by user
|
|
filteredAlbums = albums.Where(album => album.Assets != null && (album.UserOwnerId == uid || album.Assets.Any(asset => asset.IsPubliclyShared && asset.DeletedAt == null))).ToList();
|
|
// Add shared albums
|
|
filteredAlbums.AddRange(albums.Where(album => album.Assets != null && album.Assets.Any(x => x.DeletedAt == null && x.SharedWith!.Any(user => user.Id == uid))));
|
|
return Ok(filteredAlbums.Select(x => x.ToAlbumPreviewDto()).ToList());
|
|
case EAccessLevel.Curator:
|
|
case EAccessLevel.Admin:
|
|
// All albums
|
|
return Ok(albums.Select(x => x.ToAlbumPreviewDto()).ToList());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all albums that have no person assigned.
|
|
/// </summary>
|
|
/// <returns>A list of unassigned album previews.</returns>
|
|
[HttpGet("unassigned")]
|
|
public ActionResult<List<AlbumPreviewDto>> GetUnassigned() {
|
|
var albums = albumRepository.FindUnassigned().ToList();
|
|
return Ok(albums.Select(x => x.ToAlbumPreviewDto()).ToList());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates an existing album.
|
|
/// </summary>
|
|
/// <param name="id">The album ID.</param>
|
|
/// <param name="albumDto">The album update data.</param>
|
|
/// <returns>200 on success, 403 if forbidden, 404 if not found.</returns>
|
|
[HttpPost("{id}")]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public ActionResult Update([FromRoute] Guid id, [FromBody] AlbumUpdateDto albumDto) {
|
|
Guid? uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
var album = albumRepository.Find(id);
|
|
|
|
// If the album does not exist, return a 404
|
|
if (album == null) return NotFound();
|
|
|
|
switch (accessLevel) {
|
|
case EAccessLevel.User:
|
|
// Users can't update albums
|
|
return Forbid();
|
|
case EAccessLevel.Curator:
|
|
// Curators can only update albums they own
|
|
if (album.UserOwnerId != uid) return Forbid();
|
|
break;
|
|
case EAccessLevel.Admin:
|
|
// Admins can update any album
|
|
break;
|
|
}
|
|
|
|
// Update album properties
|
|
album.Title = albumDto.Name ?? album.Title;
|
|
album.UpdatedAt = DateTime.UtcNow;
|
|
album.PersonOwnerId = albumDto.Person ?? album.PersonOwnerId;
|
|
album.UserOwnerId = albumDto.Owner ?? album.UserOwnerId;
|
|
album.Assets = albumDto.Assets != null ? assetRepository.FindBulk(albumDto.Assets).ToList() : album.Assets;
|
|
album.CoverAssetId = albumDto.CoverAssetId ?? album.CoverAssetId;
|
|
|
|
// Save changes
|
|
albumRepository.Update(album);
|
|
albumRepository.Save();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates multiple albums in a bulk operation.
|
|
/// </summary>
|
|
/// <param name="bulkDto">The bulk update data with album IDs and shared values.</param>
|
|
/// <returns>200 on success, 403 if forbidden.</returns>
|
|
[HttpPost]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public ActionResult BulkUpdate([FromBody] BulkDto<AlbumUpdateDto> bulkDto) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
var albums = albumRepository.FindBulk(bulkDto.Ids).ToList();
|
|
|
|
switch (accessLevel) {
|
|
case EAccessLevel.User:
|
|
// Users can't update albums
|
|
return Forbid();
|
|
case EAccessLevel.Curator:
|
|
// Curators can only update albums they own
|
|
albums = albums.Where(x => x.UserOwnerId == uid).ToList();
|
|
break;
|
|
case EAccessLevel.Admin:
|
|
// Admins can update any album
|
|
break;
|
|
}
|
|
|
|
foreach (var album in albums) {
|
|
album.Title = bulkDto.Data.Name ?? album.Title;
|
|
album.UpdatedAt = DateTime.UtcNow;
|
|
album.PersonOwnerId = bulkDto.Data.Person ?? album.PersonOwnerId;
|
|
album.UserOwnerId = bulkDto.Data.Owner ?? album.UserOwnerId;
|
|
album.Assets = bulkDto.Data.Assets != null ? assetRepository.FindBulk(bulkDto.Data.Assets).ToList() : album.Assets;
|
|
album.CoverAssetId = bulkDto.Data.CoverAssetId ?? album.CoverAssetId;
|
|
}
|
|
|
|
albumRepository.UpdateBulk(albums);
|
|
albumRepository.Save();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new album.
|
|
/// </summary>
|
|
/// <param name="albumDto">The album creation data.</param>
|
|
/// <returns>200 on success.</returns>
|
|
[HttpPut]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public ActionResult Create([FromBody] AlbumCreateDto albumDto) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
// If not admin, the owner of a new album is the current user
|
|
albumDto.Owner = accessLevel == EAccessLevel.Admin ? albumDto.Owner : uid;
|
|
|
|
var assets = albumDto.Assets != null ? assetRepository.FindBulk(albumDto.Assets).ToList() : new List<Asset>();
|
|
|
|
var album = new Album {
|
|
Id = Guid.NewGuid(),
|
|
Title = albumDto.Name,
|
|
CreatedAt = DateTime.UtcNow,
|
|
PersonOwnerId = albumDto.Person ?? null,
|
|
UserOwnerId = albumDto.Owner,
|
|
CoverAssetId = albumDto.CoverAssetId ?? assets.OrderBy(a => a.OriginalFilename).FirstOrDefault()?.Id,
|
|
Assets = assets
|
|
};
|
|
|
|
albumRepository.Insert(album);
|
|
albumRepository.Save();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes multiple albums (soft delete).
|
|
/// </summary>
|
|
/// <param name="albumIds">The list of album IDs to remove.</param>
|
|
/// <returns>200 on success, 403 if forbidden.</returns>
|
|
[HttpDelete]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public ActionResult BulkDelete([FromBody] List<Guid> albumIds) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
var albums = albumRepository.FindBulk(albumIds).ToList();
|
|
|
|
// If the user is not an admin, filter out albums they do not own
|
|
if (accessLevel != EAccessLevel.Admin) { albums = albums.Where(x => x.UserOwnerId == uid).ToList(); }
|
|
|
|
// If no albums are left after filtering, return a 403
|
|
if (albumIds.Count == 0) return Forbid();
|
|
|
|
albums.ForEach(albumRepository.Remove);
|
|
albumRepository.Save();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an album by its ID (soft delete).
|
|
/// </summary>
|
|
/// <param name="id">The album ID.</param>
|
|
/// <returns>200 on success, 404 if not found, 403 if forbidden.</returns>
|
|
[HttpDelete("{id}")]
|
|
[Authorize(Roles = "Admin, Curator")]
|
|
public ActionResult Delete(Guid id) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
var album = albumRepository.Find(id);
|
|
|
|
// If the album does not exist, return a 404
|
|
if (album == null) return NotFound();
|
|
// If the user is not the owner or an admin, return a 403
|
|
if (accessLevel != EAccessLevel.Admin && album.UserOwnerId != uid) return Forbid();
|
|
|
|
albumRepository.Remove(album);
|
|
albumRepository.Save();
|
|
|
|
return Ok();
|
|
}
|
|
}
|