feat(backend): add pride flag models
This commit is contained in:
		
							parent
							
								
									39b0917585
								
							
						
					
					
						commit
						a70078995b
					
				
					 9 changed files with 346 additions and 0 deletions
				
			
		
							
								
								
									
										28
									
								
								Foxnouns.Backend/Controllers/FlagsController.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Foxnouns.Backend/Controllers/FlagsController.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| using Foxnouns.Backend.Database; | ||||
| using Foxnouns.Backend.Middleware; | ||||
| using Foxnouns.Backend.Services; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| 
 | ||||
| namespace Foxnouns.Backend.Controllers; | ||||
| 
 | ||||
| [Route("/api/v2/users/@me/flags")] | ||||
| public class FlagsController(DatabaseContext db, UserRendererService userRenderer) : ApiControllerBase | ||||
| { | ||||
|     [HttpGet] | ||||
|     [Authorize("identify")] | ||||
|     [ProducesResponseType<IEnumerable<PrideFlagResponse>>(statusCode: StatusCodes.Status200OK)] | ||||
|     public async Task<IActionResult> GetFlagsAsync(CancellationToken ct = default) | ||||
|     { | ||||
|         var flags = await db.PrideFlags.Where(f => f.UserId == CurrentUser!.Id).ToListAsync(ct); | ||||
| 
 | ||||
|         return Ok(flags.Select(f => new PrideFlagResponse( | ||||
|             f.Id, userRenderer.ImageUrlFor(f), f.Name, f.Description))); | ||||
|     } | ||||
| 
 | ||||
|     private record PrideFlagResponse( | ||||
|         Snowflake Id, | ||||
|         string ImageUrl, | ||||
|         string Name, | ||||
|         string? Description); | ||||
| } | ||||
|  | @ -22,6 +22,10 @@ public class DatabaseContext : DbContext | |||
|     public DbSet<Application> Applications { get; set; } | ||||
|     public DbSet<TemporaryKey> TemporaryKeys { get; set; } | ||||
|      | ||||
|     public DbSet<PrideFlag> PrideFlags { get; set; } | ||||
|     public DbSet<UserFlag> UserFlags { get; set; } | ||||
|     public DbSet<MemberFlag> MemberFlags { get; set; } | ||||
| 
 | ||||
|     public DatabaseContext(Config config, ILoggerFactory? loggerFactory) | ||||
|     { | ||||
|         var connString = new NpgsqlConnectionStringBuilder(config.Database.Url) | ||||
|  | @ -77,6 +81,9 @@ public class DatabaseContext : DbContext | |||
|         modelBuilder.Entity<Member>().Property(m => m.Names).HasColumnType("jsonb"); | ||||
|         modelBuilder.Entity<Member>().Property(m => m.Pronouns).HasColumnType("jsonb"); | ||||
| 
 | ||||
|         modelBuilder.Entity<UserFlag>().Navigation(f => f.PrideFlag).AutoInclude(); | ||||
|         modelBuilder.Entity<MemberFlag>().Navigation(f => f.PrideFlag).AutoInclude(); | ||||
| 
 | ||||
|         modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(FindFreeUserSid))!) | ||||
|             .HasName("find_free_user_sid"); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										129
									
								
								Foxnouns.Backend/Database/Migrations/20240926180037_AddFlags.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								Foxnouns.Backend/Database/Migrations/20240926180037_AddFlags.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,129 @@ | |||
| using Microsoft.EntityFrameworkCore.Migrations; | ||||
| using Microsoft.EntityFrameworkCore.Infrastructure; | ||||
| using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; | ||||
| 
 | ||||
| #nullable disable | ||||
| 
 | ||||
| namespace Foxnouns.Backend.Database.Migrations | ||||
| { | ||||
|     /// <inheritdoc /> | ||||
|     [DbContext(typeof(DatabaseContext))] | ||||
|     [Migration("20240926180037_AddFlags")] | ||||
|     public partial class AddFlags : Migration | ||||
|     { | ||||
|         /// <inheritdoc /> | ||||
|         protected override void Up(MigrationBuilder migrationBuilder) | ||||
|         { | ||||
|             migrationBuilder.CreateTable( | ||||
|                 name: "pride_flags", | ||||
|                 columns: table => new | ||||
|                 { | ||||
|                     id = table.Column<long>(type: "bigint", nullable: false), | ||||
|                     user_id = table.Column<long>(type: "bigint", nullable: false), | ||||
|                     hash = table.Column<string>(type: "text", nullable: false), | ||||
|                     name = table.Column<string>(type: "text", nullable: false), | ||||
|                     description = table.Column<string>(type: "text", nullable: true) | ||||
|                 }, | ||||
|                 constraints: table => | ||||
|                 { | ||||
|                     table.PrimaryKey("pk_pride_flags", x => x.id); | ||||
|                     table.ForeignKey( | ||||
|                         name: "fk_pride_flags_users_user_id", | ||||
|                         column: x => x.user_id, | ||||
|                         principalTable: "users", | ||||
|                         principalColumn: "id", | ||||
|                         onDelete: ReferentialAction.Cascade); | ||||
|                 }); | ||||
| 
 | ||||
|             migrationBuilder.CreateTable( | ||||
|                 name: "member_flags", | ||||
|                 columns: table => new | ||||
|                 { | ||||
|                     id = table.Column<long>(type: "bigint", nullable: false) | ||||
|                         .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), | ||||
|                     member_id = table.Column<long>(type: "bigint", nullable: false), | ||||
|                     pride_flag_id = table.Column<long>(type: "bigint", nullable: false) | ||||
|                 }, | ||||
|                 constraints: table => | ||||
|                 { | ||||
|                     table.PrimaryKey("pk_member_flags", x => x.id); | ||||
|                     table.ForeignKey( | ||||
|                         name: "fk_member_flags_members_member_id", | ||||
|                         column: x => x.member_id, | ||||
|                         principalTable: "members", | ||||
|                         principalColumn: "id", | ||||
|                         onDelete: ReferentialAction.Cascade); | ||||
|                     table.ForeignKey( | ||||
|                         name: "fk_member_flags_pride_flags_pride_flag_id", | ||||
|                         column: x => x.pride_flag_id, | ||||
|                         principalTable: "pride_flags", | ||||
|                         principalColumn: "id", | ||||
|                         onDelete: ReferentialAction.Cascade); | ||||
|                 }); | ||||
| 
 | ||||
|             migrationBuilder.CreateTable( | ||||
|                 name: "user_flags", | ||||
|                 columns: table => new | ||||
|                 { | ||||
|                     id = table.Column<long>(type: "bigint", nullable: false) | ||||
|                         .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), | ||||
|                     user_id = table.Column<long>(type: "bigint", nullable: false), | ||||
|                     pride_flag_id = table.Column<long>(type: "bigint", nullable: false) | ||||
|                 }, | ||||
|                 constraints: table => | ||||
|                 { | ||||
|                     table.PrimaryKey("pk_user_flags", x => x.id); | ||||
|                     table.ForeignKey( | ||||
|                         name: "fk_user_flags_pride_flags_pride_flag_id", | ||||
|                         column: x => x.pride_flag_id, | ||||
|                         principalTable: "pride_flags", | ||||
|                         principalColumn: "id", | ||||
|                         onDelete: ReferentialAction.Cascade); | ||||
|                     table.ForeignKey( | ||||
|                         name: "fk_user_flags_users_user_id", | ||||
|                         column: x => x.user_id, | ||||
|                         principalTable: "users", | ||||
|                         principalColumn: "id", | ||||
|                         onDelete: ReferentialAction.Cascade); | ||||
|                 }); | ||||
| 
 | ||||
|             migrationBuilder.CreateIndex( | ||||
|                 name: "ix_member_flags_member_id", | ||||
|                 table: "member_flags", | ||||
|                 column: "member_id"); | ||||
| 
 | ||||
|             migrationBuilder.CreateIndex( | ||||
|                 name: "ix_member_flags_pride_flag_id", | ||||
|                 table: "member_flags", | ||||
|                 column: "pride_flag_id"); | ||||
| 
 | ||||
|             migrationBuilder.CreateIndex( | ||||
|                 name: "ix_pride_flags_user_id", | ||||
|                 table: "pride_flags", | ||||
|                 column: "user_id"); | ||||
| 
 | ||||
|             migrationBuilder.CreateIndex( | ||||
|                 name: "ix_user_flags_pride_flag_id", | ||||
|                 table: "user_flags", | ||||
|                 column: "pride_flag_id"); | ||||
| 
 | ||||
|             migrationBuilder.CreateIndex( | ||||
|                 name: "ix_user_flags_user_id", | ||||
|                 table: "user_flags", | ||||
|                 column: "user_id"); | ||||
|         } | ||||
| 
 | ||||
|         /// <inheritdoc /> | ||||
|         protected override void Down(MigrationBuilder migrationBuilder) | ||||
|         { | ||||
|             migrationBuilder.DropTable( | ||||
|                 name: "member_flags"); | ||||
| 
 | ||||
|             migrationBuilder.DropTable( | ||||
|                 name: "user_flags"); | ||||
| 
 | ||||
|             migrationBuilder.DropTable( | ||||
|                 name: "pride_flags"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -204,6 +204,68 @@ namespace Foxnouns.Backend.Database.Migrations | |||
|                     b.ToTable("members", (string)null); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.MemberFlag", b => | ||||
|                 { | ||||
|                     b.Property<long>("Id") | ||||
|                         .ValueGeneratedOnAdd() | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("id"); | ||||
| 
 | ||||
|                     NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id")); | ||||
| 
 | ||||
|                     b.Property<long>("MemberId") | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("member_id"); | ||||
| 
 | ||||
|                     b.Property<long>("PrideFlagId") | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("pride_flag_id"); | ||||
| 
 | ||||
|                     b.HasKey("Id") | ||||
|                         .HasName("pk_member_flags"); | ||||
| 
 | ||||
|                     b.HasIndex("MemberId") | ||||
|                         .HasDatabaseName("ix_member_flags_member_id"); | ||||
| 
 | ||||
|                     b.HasIndex("PrideFlagId") | ||||
|                         .HasDatabaseName("ix_member_flags_pride_flag_id"); | ||||
| 
 | ||||
|                     b.ToTable("member_flags", (string)null); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.PrideFlag", b => | ||||
|                 { | ||||
|                     b.Property<long>("Id") | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("id"); | ||||
| 
 | ||||
|                     b.Property<string>("Description") | ||||
|                         .HasColumnType("text") | ||||
|                         .HasColumnName("description"); | ||||
| 
 | ||||
|                     b.Property<string>("Hash") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnType("text") | ||||
|                         .HasColumnName("hash"); | ||||
| 
 | ||||
|                     b.Property<string>("Name") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnType("text") | ||||
|                         .HasColumnName("name"); | ||||
| 
 | ||||
|                     b.Property<long>("UserId") | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("user_id"); | ||||
| 
 | ||||
|                     b.HasKey("Id") | ||||
|                         .HasName("pk_pride_flags"); | ||||
| 
 | ||||
|                     b.HasIndex("UserId") | ||||
|                         .HasDatabaseName("ix_pride_flags_user_id"); | ||||
| 
 | ||||
|                     b.ToTable("pride_flags", (string)null); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.TemporaryKey", b => | ||||
|                 { | ||||
|                     b.Property<long>("Id") | ||||
|  | @ -391,6 +453,35 @@ namespace Foxnouns.Backend.Database.Migrations | |||
|                     b.ToTable("users", (string)null); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.UserFlag", b => | ||||
|                 { | ||||
|                     b.Property<long>("Id") | ||||
|                         .ValueGeneratedOnAdd() | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("id"); | ||||
| 
 | ||||
|                     NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id")); | ||||
| 
 | ||||
|                     b.Property<long>("PrideFlagId") | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("pride_flag_id"); | ||||
| 
 | ||||
|                     b.Property<long>("UserId") | ||||
|                         .HasColumnType("bigint") | ||||
|                         .HasColumnName("user_id"); | ||||
| 
 | ||||
|                     b.HasKey("Id") | ||||
|                         .HasName("pk_user_flags"); | ||||
| 
 | ||||
|                     b.HasIndex("PrideFlagId") | ||||
|                         .HasDatabaseName("ix_user_flags_pride_flag_id"); | ||||
| 
 | ||||
|                     b.HasIndex("UserId") | ||||
|                         .HasDatabaseName("ix_user_flags_user_id"); | ||||
| 
 | ||||
|                     b.ToTable("user_flags", (string)null); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.AuthMethod", b => | ||||
|                 { | ||||
|                     b.HasOne("Foxnouns.Backend.Database.Models.FediverseApplication", "FediverseApplication") | ||||
|  | @ -422,6 +513,35 @@ namespace Foxnouns.Backend.Database.Migrations | |||
|                     b.Navigation("User"); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.MemberFlag", b => | ||||
|                 { | ||||
|                     b.HasOne("Foxnouns.Backend.Database.Models.Member", null) | ||||
|                         .WithMany("ProfileFlags") | ||||
|                         .HasForeignKey("MemberId") | ||||
|                         .OnDelete(DeleteBehavior.Cascade) | ||||
|                         .IsRequired() | ||||
|                         .HasConstraintName("fk_member_flags_members_member_id"); | ||||
| 
 | ||||
|                     b.HasOne("Foxnouns.Backend.Database.Models.PrideFlag", "PrideFlag") | ||||
|                         .WithMany() | ||||
|                         .HasForeignKey("PrideFlagId") | ||||
|                         .OnDelete(DeleteBehavior.Cascade) | ||||
|                         .IsRequired() | ||||
|                         .HasConstraintName("fk_member_flags_pride_flags_pride_flag_id"); | ||||
| 
 | ||||
|                     b.Navigation("PrideFlag"); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.PrideFlag", b => | ||||
|                 { | ||||
|                     b.HasOne("Foxnouns.Backend.Database.Models.User", null) | ||||
|                         .WithMany("Flags") | ||||
|                         .HasForeignKey("UserId") | ||||
|                         .OnDelete(DeleteBehavior.Cascade) | ||||
|                         .IsRequired() | ||||
|                         .HasConstraintName("fk_pride_flags_users_user_id"); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.Token", b => | ||||
|                 { | ||||
|                     b.HasOne("Foxnouns.Backend.Database.Models.Application", "Application") | ||||
|  | @ -443,11 +563,39 @@ namespace Foxnouns.Backend.Database.Migrations | |||
|                     b.Navigation("User"); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.UserFlag", b => | ||||
|                 { | ||||
|                     b.HasOne("Foxnouns.Backend.Database.Models.PrideFlag", "PrideFlag") | ||||
|                         .WithMany() | ||||
|                         .HasForeignKey("PrideFlagId") | ||||
|                         .OnDelete(DeleteBehavior.Cascade) | ||||
|                         .IsRequired() | ||||
|                         .HasConstraintName("fk_user_flags_pride_flags_pride_flag_id"); | ||||
| 
 | ||||
|                     b.HasOne("Foxnouns.Backend.Database.Models.User", null) | ||||
|                         .WithMany("ProfileFlags") | ||||
|                         .HasForeignKey("UserId") | ||||
|                         .OnDelete(DeleteBehavior.Cascade) | ||||
|                         .IsRequired() | ||||
|                         .HasConstraintName("fk_user_flags_users_user_id"); | ||||
| 
 | ||||
|                     b.Navigation("PrideFlag"); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.Member", b => | ||||
|                 { | ||||
|                     b.Navigation("ProfileFlags"); | ||||
|                 }); | ||||
| 
 | ||||
|             modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b => | ||||
|                 { | ||||
|                     b.Navigation("AuthMethods"); | ||||
| 
 | ||||
|                     b.Navigation("Flags"); | ||||
| 
 | ||||
|                     b.Navigation("Members"); | ||||
| 
 | ||||
|                     b.Navigation("ProfileFlags"); | ||||
|                 }); | ||||
| #pragma warning restore 612, 618 | ||||
|         } | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ public class Member : BaseModel | |||
|     public List<Pronoun> Pronouns { get; set; } = []; | ||||
|     public List<Field> Fields { get; set; } = []; | ||||
| 
 | ||||
|     public List<MemberFlag> ProfileFlags { get; set; } = []; | ||||
| 
 | ||||
|     public Snowflake UserId { get; init; } | ||||
|     public User User { get; init; } = null!; | ||||
| } | ||||
							
								
								
									
										25
									
								
								Foxnouns.Backend/Database/Models/PrideFlag.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Foxnouns.Backend/Database/Models/PrideFlag.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| namespace Foxnouns.Backend.Database.Models; | ||||
| 
 | ||||
| public class PrideFlag : BaseModel | ||||
| { | ||||
|     public required Snowflake UserId { get; init; } | ||||
|     public required string Hash { get; init; } | ||||
|     public required string Name { get; set; } | ||||
|     public string? Description { get; set; } | ||||
| } | ||||
| 
 | ||||
| public class UserFlag | ||||
| { | ||||
|     public long Id { get; init; } | ||||
|     public required Snowflake UserId { get; init; } | ||||
|     public required Snowflake PrideFlagId { get; init; } | ||||
|     public PrideFlag PrideFlag { get; init; } = null!; | ||||
| } | ||||
| 
 | ||||
| public class MemberFlag | ||||
| { | ||||
|     public long Id { get; init; } | ||||
|     public required Snowflake MemberId { get; init; } | ||||
|     public required Snowflake PrideFlagId { get; init; } | ||||
|     public PrideFlag PrideFlag { get; init; } = null!; | ||||
| } | ||||
|  | @ -21,6 +21,9 @@ public class User : BaseModel | |||
|     public List<Field> Fields { get; set; } = []; | ||||
|     public Dictionary<Snowflake, CustomPreference> CustomPreferences { get; set; } = []; | ||||
| 
 | ||||
|     public List<PrideFlag> Flags { get; set; } = []; | ||||
|     public List<UserFlag> ProfileFlags { get; set; } = []; | ||||
| 
 | ||||
|     public UserRole Role { get; set; } = UserRole.User; | ||||
|     public string? Password { get; set; } // Password may be null if the user doesn't authenticate with an email address | ||||
| 
 | ||||
|  |  | |||
|  | @ -47,6 +47,8 @@ public class MemberRendererService(DatabaseContext db, Config config) | |||
|     private string? AvatarUrlFor(User user) => | ||||
|         user.Avatar != null ? $"{config.MediaBaseUrl}/users/{user.Id}/avatars/{user.Avatar}.webp" : null; | ||||
| 
 | ||||
|     private string ImageUrlFor(PrideFlag flag) => $"{config.MediaBaseUrl}/flags/{flag.Hash}.webp"; | ||||
| 
 | ||||
|     public record PartialMember( | ||||
|         Snowflake Id, | ||||
|         string Sid, | ||||
|  |  | |||
|  | @ -59,6 +59,8 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe | |||
|     private string? AvatarUrlFor(User user) => | ||||
|         user.Avatar != null ? $"{config.MediaBaseUrl}/users/{user.Id}/avatars/{user.Avatar}.webp" : null; | ||||
|      | ||||
|     public string ImageUrlFor(PrideFlag flag) => $"{config.MediaBaseUrl}/flags/{flag.Hash}.webp"; | ||||
| 
 | ||||
|     public record UserResponse( | ||||
|         Snowflake Id, | ||||
|         string Sid, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue