91 lines
3.7 KiB
C#
91 lines
3.7 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|||
|
|
using Microsoft.AspNetCore.Mvc;
|
||
|
|
|
||
|
|
using MongoDB.Entities;
|
||
|
|
|
||
|
|
using PrivaPub.Api.Mastodon.Infrastructure;
|
||
|
|
using PrivaPub.Domain.Media;
|
||
|
|
using PrivaPub.Models.Media;
|
||
|
|
|
||
|
|
using System.Globalization;
|
||
|
|
|
||
|
|
namespace PrivaPub.Api.Mastodon.Controllers
|
||
|
|
{
|
||
|
|
public class MediaController : MastodonController
|
||
|
|
{
|
||
|
|
const long UploadLimit = 100L * 1024 * 1024;
|
||
|
|
|
||
|
|
readonly IMediaService _media;
|
||
|
|
readonly IMediaProxy _proxy;
|
||
|
|
|
||
|
|
public MediaController(IMediaService media, IMediaProxy proxy)
|
||
|
|
{
|
||
|
|
_media = media;
|
||
|
|
_proxy = proxy;
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpPost("/api/v1/media"), HttpPost("/api/v2/media"), Scope("write:media"), RequestSizeLimit(UploadLimit),
|
||
|
|
RequestFormLimits(MultipartBodyLengthLimit = UploadLimit)]
|
||
|
|
public async Task<IActionResult> Upload(CancellationToken token)
|
||
|
|
{
|
||
|
|
if (!Request.HasFormContentType)
|
||
|
|
return Error(StatusCodes.Status422UnprocessableEntity, "Validation failed: File can't be blank");
|
||
|
|
var form = await Request.ReadFormAsync(token);
|
||
|
|
var outcome = await _media.Upload(Me, form.Files["file"], form["description"], form["focus"], token);
|
||
|
|
return outcome.Ok ? Json(View(outcome.Attachment)) : Error(outcome.Status, outcome.Error);
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("/api/v1/media/{id}"), Scope("write:media")]
|
||
|
|
public async Task<IActionResult> Get(string id, CancellationToken token)
|
||
|
|
{
|
||
|
|
var attachment = await DB.Default.Find<MediaAttachment>().Match(m => m.ID == id && m.OwnerAvatarId == MyId).ExecuteFirstAsync(token);
|
||
|
|
return attachment == default ? NotFoundError() : Json(View(attachment));
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpPut("/api/v1/media/{id}"), Scope("write:media")]
|
||
|
|
public async Task<IActionResult> Update(string id, CancellationToken token)
|
||
|
|
{
|
||
|
|
var attachment = await DB.Default.Find<MediaAttachment>().Match(m => m.ID == id && m.OwnerAvatarId == MyId).ExecuteFirstAsync(token);
|
||
|
|
if (attachment == default)
|
||
|
|
return NotFoundError();
|
||
|
|
if (Params.Has("description"))
|
||
|
|
attachment.Description = Params.Get("description")?.Trim() is { Length: > 0 } description ? description[..Math.Min(description.Length, 1500)] : default;
|
||
|
|
if (Params.Get("focus")?.Split(',') is [var x, var y]
|
||
|
|
&& float.TryParse(x, NumberStyles.Float, CultureInfo.InvariantCulture, out var fx) && float.TryParse(y, NumberStyles.Float, CultureInfo.InvariantCulture, out var fy))
|
||
|
|
attachment.Focus = new[] { Math.Clamp(fx, -1, 1), Math.Clamp(fy, -1, 1) };
|
||
|
|
await DB.Default.SaveAsync(attachment, token);
|
||
|
|
return Json(View(attachment));
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("/media/proxy/{signature}/{encoded}"), AllowAnonymous, ApiExplorerSettings(IgnoreApi = true)]
|
||
|
|
public async Task<IActionResult> Proxy(string signature, string encoded, CancellationToken token)
|
||
|
|
{
|
||
|
|
var (path, contentType) = await _proxy.Fetch(signature, encoded, token);
|
||
|
|
if (path == default)
|
||
|
|
return NotFound();
|
||
|
|
Response.Headers["X-Content-Type-Options"] = "nosniff";
|
||
|
|
Response.Headers["Content-Security-Policy"] = "default-src 'none'; sandbox";
|
||
|
|
Response.Headers["Cache-Control"] = "public, max-age=604800";
|
||
|
|
return PhysicalFile(path, contentType);
|
||
|
|
}
|
||
|
|
|
||
|
|
object View(MediaAttachment attachment) => new
|
||
|
|
{
|
||
|
|
id = attachment.ID,
|
||
|
|
type = attachment.Kind,
|
||
|
|
url = _media.Url(attachment.FilePath),
|
||
|
|
preview_url = _media.Url(attachment.PreviewPath ?? attachment.FilePath),
|
||
|
|
remote_url = default(string),
|
||
|
|
text_url = default(string),
|
||
|
|
meta = new
|
||
|
|
{
|
||
|
|
original = attachment.Width.HasValue && attachment.Height > 0
|
||
|
|
? new { width = attachment.Width, height = attachment.Height, size = $"{attachment.Width}x{attachment.Height}", aspect = (double)attachment.Width / attachment.Height.Value }
|
||
|
|
: default,
|
||
|
|
focus = attachment.Focus is { Length: 2 } ? new { x = attachment.Focus[0], y = attachment.Focus[1] } : default
|
||
|
|
},
|
||
|
|
description = attachment.Description,
|
||
|
|
blurhash = attachment.Blurhash
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|