307 lines
11 KiB
C#
307 lines
11 KiB
C#
using Lactose.Dtos;
|
|
using Lactose.Dtos.Asset;
|
|
using Lactose.Mapper;
|
|
using Lactose.Models;
|
|
using Lactose.Repositories;
|
|
using Lactose.Services;
|
|
using Lactose.Types;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|
|
|
namespace Lactose.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public class AssetController(
|
|
ILogger<AssetController> logger,
|
|
LactoseAuthService authService,
|
|
IAssetRepository assetRepository,
|
|
IUserRepository userRepository
|
|
) : ControllerBase {
|
|
[HttpGet("{id}")]
|
|
public ActionResult<AssetDto> Get(Guid id) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
EAccessLevel accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
logger.LogTrace(
|
|
$"""
|
|
Requested asset: {id}
|
|
by user: {uid}
|
|
access level: {accessLevel}
|
|
"""
|
|
);
|
|
|
|
var asset = assetRepository.Find(id);
|
|
|
|
if (asset == null) {
|
|
logger.LogWarning($"Request asset {id} not found in the database!");
|
|
return NotFound();
|
|
}
|
|
|
|
switch (accessLevel) {
|
|
case EAccessLevel.User: {
|
|
//Not publicly shared && Not shared with the user
|
|
if (!asset.IsPubliclyShared && asset.SharedWith?.Find(x => x.Id == uid) == null) {
|
|
logger.LogTrace($"{asset.Id} is not shared with user {uid}");
|
|
return Unauthorized();
|
|
}
|
|
|
|
//Deleted asset
|
|
if (asset.DeletedAt != null) {
|
|
logger.LogTrace($"{asset.Id} is deleted and thus not exists for this user");
|
|
return NotFound();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case EAccessLevel.Curator: {
|
|
//Deleted asset owned by the user
|
|
if (asset.DeletedAt != null)
|
|
if (asset.Owner?.Id != uid) {
|
|
logger.LogTrace(
|
|
$"{asset.Id} is deleted and it's not owned by this user so it does not exists for this user"
|
|
);
|
|
|
|
return NotFound();
|
|
} else { logger.LogTrace($"{asset.Id} is deleted but it's owned by this user so it exists for this user"); }
|
|
|
|
break;
|
|
}
|
|
case EAccessLevel.Admin: {
|
|
//Admins will see this regardless
|
|
logger.LogTrace($"{asset.Id} is being requested by an admin, sending it regardless of the visibility state");
|
|
break;
|
|
}
|
|
}
|
|
|
|
AssetDto dto = asset.ToFullAssetsDto(accessLevel);
|
|
|
|
logger.LogTrace(
|
|
$"""
|
|
Outbound DTO:
|
|
dto.Id: {dto.Id}
|
|
dto.FileName: {dto.FileName}
|
|
dto.AssetType: {dto.AssetType}
|
|
dto.IsPublic: {dto.IsPublic}
|
|
dto.CreatedAt: {dto.CreatedAt}
|
|
dto.UpdatedAt: {dto.UpdatedAt}
|
|
dto.DeletedAt: {dto.DeletedAt}
|
|
dto.Owner: {dto.Owner.Id} {dto.Owner.Username}
|
|
"""
|
|
);
|
|
|
|
return Ok(dto);
|
|
}
|
|
|
|
[HttpGet]
|
|
public ActionResult<List<AssetPreviewDto>> GetAll([FromQuery] AssetSearchOptionsDto searchOptionsDto) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
EAccessLevel accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
DateTime from = DateTime.Parse(searchOptionsDto.StartDate ?? string.Empty);
|
|
DateTime to = DateTime.Parse(searchOptionsDto.EndDate ?? string.Empty);
|
|
|
|
if (from > to) {
|
|
logger.LogWarning("Invalid date range provided");
|
|
return BadRequest();
|
|
}
|
|
|
|
if (searchOptionsDto.Page < 1 || searchOptionsDto.PageSize < 1) {
|
|
logger.LogWarning("Invalid pagination parameters provided");
|
|
return BadRequest();
|
|
}
|
|
|
|
var assets = assetRepository.FindByDateRange(from, to, searchOptionsDto.Page, searchOptionsDto.PageSize);
|
|
|
|
logger.LogTrace(
|
|
$"""
|
|
Requested assets
|
|
by user: {uid}
|
|
access level: {accessLevel}
|
|
date range: {from} - {to}
|
|
"""
|
|
);
|
|
|
|
switch (accessLevel) {
|
|
case EAccessLevel.User: {
|
|
//Only publicly shared assets and assets shared with the user that are not deleted
|
|
assets = assets.Where(
|
|
a => a.IsPubliclyShared || a.SharedWith?.Find(x => x.Id == uid) != null && a.DeletedAt != null
|
|
);
|
|
|
|
break;
|
|
}
|
|
case EAccessLevel.Curator:
|
|
case EAccessLevel.Admin: {
|
|
//Admins will see this regardless
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Ok(assets.ToAssetPreviewDto());
|
|
}
|
|
|
|
[HttpPost("{id}")]
|
|
[Authorize(Roles = "Curator,Admin")]
|
|
public IStatusCodeActionResult Update([FromRoute] Guid id, [FromBody] AssetUpdateDto dto) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accesslevel = authService.GetUserData(User)!.AccessLevel;
|
|
var asset = assetRepository.Find(id);
|
|
|
|
if (asset == null) {
|
|
logger.LogWarning($"Request asset {id} not found in the database!");
|
|
return NotFound();
|
|
}
|
|
|
|
if (asset.Owner?.Id != uid && accesslevel != EAccessLevel.Admin) {
|
|
logger.LogWarning($"User {authService.GetUserData(User)?.Id} is trying to update asset {id} that they do not own");
|
|
return Unauthorized();
|
|
}
|
|
|
|
string log = "";
|
|
|
|
asset.IsPubliclyShared = dto.IsPublic ?? asset.IsPubliclyShared;
|
|
log += $"PubliclyShared: {asset.IsPubliclyShared} -> {dto.IsPublic ?? asset.IsPubliclyShared}\n";
|
|
|
|
asset.DeletedAt = dto.DeletedAt ?? asset.DeletedAt;
|
|
log += $"DeletedAt: {asset.DeletedAt} -> {dto.DeletedAt ?? asset.DeletedAt}\n";
|
|
|
|
asset.Owner = dto.Owner != null ? userRepository.Find(dto.Owner.Value) : asset.Owner;
|
|
log += $"Owner: {asset.Owner?.Id} -> {dto.Owner ?? asset.Owner?.Id}\n";
|
|
|
|
logger.LogTrace($"Updating asset {id}\n{log}");
|
|
|
|
assetRepository.Update(asset);
|
|
assetRepository.Save();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
[HttpPost]
|
|
[Authorize(Roles = "Curator,Admin")]
|
|
public IStatusCodeActionResult BulkUpdate([FromBody] BulkDto<AssetUpdateDto> bulkDto) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accesslevel = authService.GetUserData(User)!.AccessLevel;
|
|
var assets = assetRepository.FindBulk(bulkDto.Ids);
|
|
|
|
var enumerable = assets as List<Asset> ?? assets.ToList();
|
|
|
|
if (enumerable.Count() != bulkDto.Ids.Count) {
|
|
logger.LogWarning(
|
|
$"Some assets were not found in the database" +
|
|
$"\nrequested assets: {bulkDto.Ids.Count}, found assets: {enumerable.Count()}"
|
|
);
|
|
|
|
var missingIds = bulkDto.Ids.Except(enumerable.Select(a => a.Id)).ToList();
|
|
logger.LogTrace($"Missing IDs: {string.Join(", ", missingIds)}");
|
|
}
|
|
|
|
//If the user is not an admin, they can only update their own assets
|
|
if (accesslevel != EAccessLevel.Admin) enumerable = enumerable.Where(x => x.OwnerId == uid).ToList();
|
|
|
|
enumerable.ForEach(
|
|
x => {
|
|
x.IsPubliclyShared = bulkDto.Data.IsPublic ?? x.IsPubliclyShared;
|
|
x.DeletedAt = bulkDto.Data.DeletedAt ?? x.DeletedAt;
|
|
x.Owner = bulkDto.Data.Owner != null ? userRepository.Find(bulkDto.Data.Owner.Value) : x.Owner;
|
|
}
|
|
);
|
|
|
|
assetRepository.UpdateBulk(enumerable);
|
|
return Ok();
|
|
}
|
|
|
|
[HttpDelete("{id}")]
|
|
[Authorize(Roles = "Curator,Admin")]
|
|
public IStatusCodeActionResult Delete([FromRoute] Guid id) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accesslevel = authService.GetUserData(User)!.AccessLevel;
|
|
var asset = assetRepository.Find(id);
|
|
|
|
if (asset == null) {
|
|
logger.LogWarning($"Request asset {id} not found in the database!");
|
|
return NotFound();
|
|
}
|
|
|
|
if (asset.Owner?.Id != uid && accesslevel != EAccessLevel.Admin) {
|
|
logger.LogWarning($"User {authService.GetUserData(User)?.Id} is trying to delete asset {id} that they do not own");
|
|
return Unauthorized();
|
|
}
|
|
|
|
asset.DeletedAt = DateTime.Now;
|
|
assetRepository.Update(asset);
|
|
assetRepository.Save();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
[HttpDelete]
|
|
[Authorize(Roles = "Curator,Admin")]
|
|
public IStatusCodeActionResult BulkDelete([FromBody] List<Guid> ids) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accesslevel = authService.GetUserData(User)!.AccessLevel;
|
|
var assets = assetRepository.FindBulk(ids);
|
|
|
|
var enumerable = assets as List<Asset> ?? assets.ToList();
|
|
|
|
if (enumerable.Count() != ids.Count) {
|
|
logger.LogWarning(
|
|
$"Some assets were not found in the database" +
|
|
$"\nrequested assets: {ids.Count}, found assets: {enumerable.Count()}"
|
|
);
|
|
|
|
var missingIds = ids.Except(enumerable.Select(a => a.Id)).ToList();
|
|
logger.LogTrace($"Missing IDs: {string.Join(", ", missingIds)}");
|
|
}
|
|
|
|
//If the user is not an admin, they can only delete their own assets
|
|
if (accesslevel != EAccessLevel.Admin) enumerable = enumerable.Where(x => x.OwnerId == uid).ToList();
|
|
|
|
enumerable.ForEach(x => x.DeletedAt = DateTime.Now);
|
|
|
|
assetRepository.UpdateBulk(enumerable);
|
|
return Ok();
|
|
}
|
|
|
|
[NonAction]
|
|
[HttpPut]
|
|
[Authorize(Roles = "Curator,Admin")]
|
|
public ActionResult<Guid> Create([FromBody] AssetCreateDto dto) {
|
|
var uid = authService.GetUserData(User)?.Id;
|
|
var accesslevel = authService.GetUserData(User)!.AccessLevel;
|
|
|
|
if (userRepository.Find(dto.Owner) == null) {
|
|
logger.LogWarning($"User {dto.Owner} does not exist in the database");
|
|
return BadRequest();
|
|
}
|
|
|
|
if (accesslevel != EAccessLevel.Admin && dto.Owner != uid) {
|
|
logger.LogWarning($"User {uid} is trying to create an asset for user {dto.Owner}");
|
|
return Unauthorized();
|
|
}
|
|
|
|
//TODO: This makes exactly zero sense. How are we supposed to create an asset externally here?
|
|
// Does this even need to exist?
|
|
var asset = new Asset {
|
|
Id = Guid.NewGuid(),
|
|
Type = dto.AssetType,
|
|
IsPubliclyShared = dto.IsPublic,
|
|
OwnerId = dto.Owner,
|
|
CreatedAt = dto.CreatedAt,
|
|
MimeType = dto.MimeType,
|
|
ResolutionWidth = dto.ResolutionWidth,
|
|
ResolutionHeight = dto.ResolutionHeight,
|
|
FileSize = dto.FileSize,
|
|
Duration = dto.Duration ?? 0,
|
|
FrameRate = dto.FrameRate ?? 0,
|
|
OriginalPath = String.Empty,
|
|
OriginalFilename = String.Empty
|
|
};
|
|
|
|
assetRepository.Insert(asset);
|
|
assetRepository.Save();
|
|
|
|
return Ok(asset.Id);
|
|
}
|
|
}
|