feat: flag management

This commit is contained in:
sam 2024-12-09 14:52:31 +01:00
parent 8bd4449804
commit d9d48c3cbf
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
24 changed files with 615 additions and 235 deletions

View file

@ -46,13 +46,22 @@ public class FlagsController(
ValidationUtils.Validate(ValidateFlag(req.Name, req.Description, req.Image));
Snowflake id = snowflakeGenerator.GenerateSnowflake();
var flag = new PrideFlag
{
Id = snowflakeGenerator.GenerateSnowflake(),
UserId = CurrentUser!.Id,
Name = req.Name,
Description = req.Description,
};
db.Add(flag);
await db.SaveChangesAsync();
queue.QueueInvocableWithPayload<CreateFlagInvocable, CreateFlagPayload>(
new CreateFlagPayload(id, CurrentUser!.Id, req.Name, req.Image, req.Description)
new CreateFlagPayload(flag.Id, CurrentUser!.Id, req.Name, req.Image, req.Description)
);
return Accepted(new CreateFlagResponse(id, req.Name, req.Description));
return Accepted(userRenderer.RenderPrideFlag(flag));
}
[HttpPatch("{id}")]

View file

@ -0,0 +1,41 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Foxnouns.Backend.Database.Migrations
{
/// <inheritdoc />
[DbContext(typeof(DatabaseContext))]
[Migration("20241209134148_NullableFlagHash")]
public partial class NullableFlagHash : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "hash",
table: "pride_flags",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "text"
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "hash",
table: "pride_flags",
type: "text",
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "text",
oldNullable: true
);
}
}
}

View file

@ -282,7 +282,6 @@ namespace Foxnouns.Backend.Database.Migrations
.HasColumnName("description");
b.Property<string>("Hash")
.IsRequired()
.HasColumnType("text")
.HasColumnName("hash");
@ -546,7 +545,7 @@ namespace Foxnouns.Backend.Database.Migrations
modelBuilder.Entity("Foxnouns.Backend.Database.Models.DataExport", b =>
{
b.HasOne("Foxnouns.Backend.Database.Models.User", "User")
.WithMany()
.WithMany("DataExports")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
@ -645,6 +644,8 @@ namespace Foxnouns.Backend.Database.Migrations
{
b.Navigation("AuthMethods");
b.Navigation("DataExports");
b.Navigation("Flags");
b.Navigation("Members");

View file

@ -3,7 +3,9 @@ namespace Foxnouns.Backend.Database.Models;
public class PrideFlag : BaseModel
{
public required Snowflake UserId { get; init; }
public required string Hash { get; init; }
// A null hash means the flag hasn't been processed yet.
public string? Hash { get; set; }
public required string Name { get; set; }
public string? Description { get; set; }
}

View file

@ -4,7 +4,7 @@ using Foxnouns.Backend.Utils;
namespace Foxnouns.Backend.Dto;
public record PrideFlagResponse(Snowflake Id, string ImageUrl, string Name, string? Description);
public record PrideFlagResponse(Snowflake Id, string? ImageUrl, string Name, string? Description);
public record CreateFlagRequest(string Name, string Image, string? Description);

View file

@ -108,6 +108,12 @@ public class CreateDataExportInvocable(
private async Task WritePrideFlag(ZipArchive zip, PrideFlag flag)
{
if (flag.Hash == null)
{
_logger.Debug("Flag {FlagId} has a null hash, ignoring it", flag.Id);
return;
}
_logger.Debug("Writing flag {FlagId}", flag.Id);
var flagData = $"""

View file

@ -3,6 +3,7 @@ using Foxnouns.Backend.Database;
using Foxnouns.Backend.Database.Models;
using Foxnouns.Backend.Extensions;
using Foxnouns.Backend.Services;
using Microsoft.EntityFrameworkCore;
namespace Foxnouns.Backend.Jobs;
@ -26,6 +27,18 @@ public class CreateFlagInvocable(
try
{
PrideFlag? flag = await db.PrideFlags.FirstOrDefaultAsync(f =>
f.Id == Payload.Id && f.UserId == Payload.UserId
);
if (flag == null)
{
_logger.Warning(
"Got a flag create job for {FlagId} but it doesn't exist, aborting",
Payload.Id
);
return;
}
(string? hash, Stream? image) = await ImageObjectExtensions.ConvertBase64UriToImage(
Payload.ImageData,
256,
@ -33,16 +46,8 @@ public class CreateFlagInvocable(
);
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);
flag.Hash = hash;
db.Update(flag);
await db.SaveChangesAsync();
_logger.Information("Uploaded flag {FlagId} with hash {Hash}", flag.Id, flag.Hash);

View file

@ -83,7 +83,8 @@ public class MemberRendererService(DatabaseContext db, Config config)
? $"{config.MediaBaseUrl}/users/{user.Id}/avatars/{user.Avatar}.webp"
: null;
private string ImageUrlFor(PrideFlag flag) => $"{config.MediaBaseUrl}/flags/{flag.Hash}.webp";
private string? ImageUrlFor(PrideFlag flag) =>
flag.Hash != null ? $"{config.MediaBaseUrl}/flags/{flag.Hash}.webp" : null;
private PrideFlagResponse RenderPrideFlag(PrideFlag flag) =>
new(flag.Id, ImageUrlFor(flag), flag.Name, flag.Description);

View file

@ -131,7 +131,8 @@ public class UserRendererService(
? $"{config.MediaBaseUrl}/users/{user.Id}/avatars/{user.Avatar}.webp"
: null;
public string ImageUrlFor(PrideFlag flag) => $"{config.MediaBaseUrl}/flags/{flag.Hash}.webp";
public string? ImageUrlFor(PrideFlag flag) =>
flag.Hash != null ? $"{config.MediaBaseUrl}/flags/{flag.Hash}.webp" : null;
public PrideFlagResponse RenderPrideFlag(PrideFlag flag) =>
new(flag.Id, ImageUrlFor(flag), flag.Name, flag.Description);