feat(backend): add create flag endpoint and job

This commit is contained in:
sam 2024-09-26 22:26:40 +02:00
parent ff2ba1fb1b
commit 14e6e35cb7
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
4 changed files with 87 additions and 5 deletions

View file

@ -1,4 +1,7 @@
using Coravel.Queuing.Interfaces;
using Foxnouns.Backend.Database; using Foxnouns.Backend.Database;
using Foxnouns.Backend.Database.Models;
using Foxnouns.Backend.Jobs;
using Foxnouns.Backend.Middleware; using Foxnouns.Backend.Middleware;
using Foxnouns.Backend.Services; using Foxnouns.Backend.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -7,7 +10,11 @@ using Microsoft.EntityFrameworkCore;
namespace Foxnouns.Backend.Controllers; namespace Foxnouns.Backend.Controllers;
[Route("/api/v2/users/@me/flags")] [Route("/api/v2/users/@me/flags")]
public class FlagsController(DatabaseContext db, UserRendererService userRenderer) : ApiControllerBase public class FlagsController(
DatabaseContext db,
UserRendererService userRenderer,
ISnowflakeGenerator snowflakeGenerator,
IQueue queue) : ApiControllerBase
{ {
[HttpGet] [HttpGet]
[Authorize("identify")] [Authorize("identify")]
@ -16,10 +23,29 @@ public class FlagsController(DatabaseContext db, UserRendererService userRendere
{ {
var flags = await db.PrideFlags.Where(f => f.UserId == CurrentUser!.Id).ToListAsync(ct); var flags = await db.PrideFlags.Where(f => f.UserId == CurrentUser!.Id).ToListAsync(ct);
return Ok(flags.Select(f => new PrideFlagResponse( return Ok(flags.Select(ToResponse));
f.Id, userRenderer.ImageUrlFor(f), f.Name, f.Description)));
} }
[HttpPost]
[Authorize("user.update")]
[ProducesResponseType<PrideFlagResponse>(statusCode: StatusCodes.Status202Accepted)]
public IActionResult CreateFlag([FromBody] CreateFlagRequest req)
{
var id = snowflakeGenerator.GenerateSnowflake();
queue.QueueInvocableWithPayload<CreateFlagInvocable, CreateFlagPayload>(
new CreateFlagPayload(id, CurrentUser!.Id, req.Name, req.Image, req.Description));
return Accepted(new CreateFlagResponse(id, req.Name, req.Description));
}
public record CreateFlagRequest(string Name, string Image, string? Description);
public record CreateFlagResponse(Snowflake Id, string Name, string? Description);
private PrideFlagResponse ToResponse(PrideFlag flag) =>
new(flag.Id, userRenderer.ImageUrlFor(flag), flag.Name, flag.Description);
private record PrideFlagResponse( private record PrideFlagResponse(
Snowflake Id, Snowflake Id,
string ImageUrl, string ImageUrl,

View file

@ -99,7 +99,8 @@ public static class WebApplicationExtensions
.AddHostedService<PeriodicTasksService>() .AddHostedService<PeriodicTasksService>()
// Transient jobs // Transient jobs
.AddTransient<MemberAvatarUpdateInvocable>() .AddTransient<MemberAvatarUpdateInvocable>()
.AddTransient<UserAvatarUpdateInvocable>(); .AddTransient<UserAvatarUpdateInvocable>()
.AddTransient<CreateFlagInvocable>();
if (!config.Logging.EnableMetrics) if (!config.Logging.EnableMetrics)
services.AddHostedService<BackgroundMetricsCollectionService>(); services.AddHostedService<BackgroundMetricsCollectionService>();

View file

@ -0,0 +1,53 @@
using System.Security.Cryptography;
using Coravel.Invocable;
using Foxnouns.Backend.Database;
using Foxnouns.Backend.Database.Models;
using Foxnouns.Backend.Extensions;
using Foxnouns.Backend.Services;
namespace Foxnouns.Backend.Jobs;
public class CreateFlagInvocable(DatabaseContext db, ObjectStorageService objectStorageService, ILogger logger)
: IInvocable, IInvocableWithPayload<CreateFlagPayload>
{
private readonly ILogger _logger = logger.ForContext<CreateFlagInvocable>();
public required CreateFlagPayload Payload { get; set; }
public async Task Invoke()
{
_logger.Information("Creating flag {FlagId} for user {UserId} with image data length {DataLength}", Payload.Id,
Payload.UserId, Payload.ImageData.Length);
try
{
var image = await Payload.ImageData.ConvertBase64UriToImage(size: 256, crop: false);
image.Seek(0, SeekOrigin.Begin);
var hash = Convert.ToHexString(await SHA256.HashDataAsync(image)).ToLower();
image.Seek(0, SeekOrigin.Begin);
await objectStorageService.PutObjectAsync(Path(hash), image, "image/webp");
var flag = new PrideFlag
{
Id = Payload.Id,
UserId = Payload.UserId,
Hash = hash,
Name = Payload.Name,
Description = Payload.Description
};
db.Add(flag);
await db.SaveChangesAsync();
_logger.Information("Uploaded flag {FlagId} with hash {Hash}", flag.Id, flag.Hash);
}
catch (ArgumentException ae)
{
_logger.Warning("Invalid data URI for flag {FlagId}: {Reason}", Payload.Id, ae.Message);
}
throw new NotImplementedException();
}
private static string Path(string hash) => $"flags/{hash}.webp";
}

View file

@ -3,3 +3,5 @@ using Foxnouns.Backend.Database;
namespace Foxnouns.Backend.Jobs; namespace Foxnouns.Backend.Jobs;
public record AvatarUpdatePayload(Snowflake Id, string? NewAvatar); public record AvatarUpdatePayload(Snowflake Id, string? NewAvatar);
public record CreateFlagPayload(Snowflake Id, Snowflake UserId, string Name, string ImageData, string? Description);