From 83f57e800ae279bdd0e18ed6c6253a9b7be946ea Mon Sep 17 00:00:00 2001 From: Yuze Fu Date: Fri, 20 Sep 2024 17:05:53 +0900 Subject: [PATCH] feat: record user email and name --- .../Controllers/AuthController.cs | 5 +- .../20240920080406_UserAddEmail.Designer.cs | 554 ++++++++++++++++++ .../Migrations/20240920080406_UserAddEmail.cs | 38 ++ .../Migrations/VATPRCContextModelSnapshot.cs | 8 + Net.Vatprc.Uniapi/Models/User.cs | 8 + .../Services/VatsimAuthService.cs | 18 + 6 files changed, 629 insertions(+), 2 deletions(-) create mode 100644 Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.Designer.cs create mode 100644 Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.cs diff --git a/Net.Vatprc.Uniapi/Controllers/AuthController.cs b/Net.Vatprc.Uniapi/Controllers/AuthController.cs index 1478fdc..8307095 100644 --- a/Net.Vatprc.Uniapi/Controllers/AuthController.cs +++ b/Net.Vatprc.Uniapi/Controllers/AuthController.cs @@ -78,11 +78,12 @@ public async Task VatsimCallback(string code, string? state) user = new() { Cid = vatsimUser.Data.Cid, - FullName = vatsimUser.Data.Cid }; DbContext.User.Add(user); - await DbContext.SaveChangesAsync(); } + user.FullName = vatsimUser.Data.Personal.FullName; + user.Email = vatsimUser.Data.Personal.Email; + await DbContext.SaveChangesAsync(); if (Request.Cookies.TryGetValue("redirect", out var redirect) && !string.IsNullOrEmpty(redirect) diff --git a/Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.Designer.cs b/Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.Designer.cs new file mode 100644 index 0000000..2d393cb --- /dev/null +++ b/Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.Designer.cs @@ -0,0 +1,554 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Net.Vatprc.Uniapi; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Net.Vatprc.Uniapi.Migrations +{ + [DbContext(typeof(VATPRCContext))] + [Migration("20240920080406_UserAddEmail")] + partial class UserAddEmail + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("EndAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("end_at"); + + b.Property("EndBookingAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("end_booking_at"); + + b.Property("ImageUrl") + .HasColumnType("text") + .HasColumnName("image_url"); + + b.Property("StartAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_at"); + + b.Property("StartBookingAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_booking_at"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_event"); + + b.ToTable("event", (string)null); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventAirspace", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("EventId") + .HasColumnType("uuid") + .HasColumnName("event_id"); + + b.Property("IcaoCodes") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("icao_codes"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_event_airspace"); + + b.HasIndex("EventId") + .HasDatabaseName("ix_event_airspace_event_id"); + + b.ToTable("event_airspace", (string)null); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventBooking", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("EventSlotId") + .HasColumnType("uuid") + .HasColumnName("event_slot_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_event_booking"); + + b.HasIndex("EventSlotId") + .IsUnique() + .HasDatabaseName("ix_event_booking_event_slot_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_event_booking_user_id"); + + b.ToTable("event_booking", (string)null); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventSlot", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AircraftTypeIcao") + .HasColumnType("text") + .HasColumnName("aircraft_type_icao"); + + b.Property("Callsign") + .HasColumnType("text") + .HasColumnName("callsign"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("EnterAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("enter_at"); + + b.Property("EventAirspaceId") + .HasColumnType("uuid") + .HasColumnName("event_airspace_id"); + + b.Property("LeaveAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("leave_at"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_event_slot"); + + b.HasIndex("EventAirspaceId") + .HasDatabaseName("ix_event_slot_event_airspace_id"); + + b.ToTable("event_slot", (string)null); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.Notam", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("EffectiveFrom") + .HasColumnType("timestamp with time zone") + .HasColumnName("effective_from"); + + b.Property("ExpireAfter") + .HasColumnType("timestamp with time zone") + .HasColumnName("expire_after"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_notam"); + + b.ToTable("notam", (string)null); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.NotamBinding", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("character varying(34)") + .HasColumnName("discriminator"); + + b.Property("NotamId") + .HasColumnType("uuid") + .HasColumnName("notam_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_notam_binding"); + + b.HasIndex("NotamId") + .HasDatabaseName("ix_notam_binding_notam_id"); + + b.ToTable("notam_binding", (string)null); + + b.HasDiscriminator("Discriminator").HasValue("NotamBinding"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.Session", b => + { + b.Property("Token") + .HasColumnType("uuid") + .HasColumnName("token"); + + b.Property("Code") + .HasColumnType("uuid") + .HasColumnName("code"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("ExpiresIn") + .HasColumnType("timestamp with time zone") + .HasColumnName("expires_in"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserUpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("user_updated_at"); + + b.HasKey("Token") + .HasName("pk_session"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_session_user_id"); + + b.ToTable("session", (string)null); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Cid") + .IsRequired() + .HasColumnType("text") + .HasColumnName("cid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FullName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("full_name"); + + b.Property("Roles") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("roles"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_user"); + + b.HasIndex("Cid") + .IsUnique() + .HasDatabaseName("ix_user_cid"); + + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("ix_user_email"); + + b.ToTable("user", (string)null); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.NotamBindingEvent", b => + { + b.HasBaseType("Net.Vatprc.Uniapi.Models.NotamBinding"); + + b.Property("EventId") + .HasColumnType("uuid") + .HasColumnName("event_id"); + + b.HasIndex("EventId") + .HasDatabaseName("ix_notam_binding_event_id"); + + b.ToTable("notam_binding", (string)null); + + b.HasDiscriminator().HasValue("NotamBindingEvent"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.NotamBindingEventAirspace", b => + { + b.HasBaseType("Net.Vatprc.Uniapi.Models.NotamBinding"); + + b.Property("EventAirspaceId") + .HasColumnType("uuid") + .HasColumnName("event_airspace_id"); + + b.HasIndex("EventAirspaceId") + .HasDatabaseName("ix_notam_binding_event_airspace_id"); + + b.ToTable("notam_binding", (string)null); + + b.HasDiscriminator().HasValue("NotamBindingEventAirspace"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.NotamBindingIcaoCode", b => + { + b.HasBaseType("Net.Vatprc.Uniapi.Models.NotamBinding"); + + b.Property("IcaoCode") + .IsRequired() + .HasColumnType("text") + .HasColumnName("icao_code"); + + b.HasIndex("IcaoCode") + .HasDatabaseName("ix_notam_binding_icao_code"); + + b.ToTable("notam_binding", (string)null); + + b.HasDiscriminator().HasValue("NotamBindingIcaoCode"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventAirspace", b => + { + b.HasOne("Net.Vatprc.Uniapi.Models.Event", "Event") + .WithMany("Airspaces") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_event_airspace_event_event_id"); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventBooking", b => + { + b.HasOne("Net.Vatprc.Uniapi.Models.EventSlot", "EventSlot") + .WithOne("Booking") + .HasForeignKey("Net.Vatprc.Uniapi.Models.EventBooking", "EventSlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_event_booking_event_slot_event_slot_id"); + + b.HasOne("Net.Vatprc.Uniapi.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_event_booking_user_user_id"); + + b.Navigation("EventSlot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventSlot", b => + { + b.HasOne("Net.Vatprc.Uniapi.Models.EventAirspace", "EventAirspace") + .WithMany("Slots") + .HasForeignKey("EventAirspaceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_event_slot_event_airspace_event_airspace_id"); + + b.Navigation("EventAirspace"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.NotamBinding", b => + { + b.HasOne("Net.Vatprc.Uniapi.Models.Notam", "Notam") + .WithMany("Bindings") + .HasForeignKey("NotamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_notam_binding_notam_notam_id"); + + b.Navigation("Notam"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.Session", b => + { + b.HasOne("Net.Vatprc.Uniapi.Models.User", "User") + .WithMany("Sessions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_session_user_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.NotamBindingEvent", b => + { + b.HasOne("Net.Vatprc.Uniapi.Models.Event", "Event") + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_notam_binding_event_event_id"); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.NotamBindingEventAirspace", b => + { + b.HasOne("Net.Vatprc.Uniapi.Models.EventAirspace", "EventAirspace") + .WithMany() + .HasForeignKey("EventAirspaceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_notam_binding_event_airspace_event_airspace_id"); + + b.Navigation("EventAirspace"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.Event", b => + { + b.Navigation("Airspaces"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventAirspace", b => + { + b.Navigation("Slots"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.EventSlot", b => + { + b.Navigation("Booking"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.Notam", b => + { + b.Navigation("Bindings"); + }); + + modelBuilder.Entity("Net.Vatprc.Uniapi.Models.User", b => + { + b.Navigation("Sessions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.cs b/Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.cs new file mode 100644 index 0000000..96ed816 --- /dev/null +++ b/Net.Vatprc.Uniapi/Migrations/20240920080406_UserAddEmail.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Net.Vatprc.Uniapi.Migrations +{ + /// + public partial class UserAddEmail : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "email", + table: "user", + type: "text", + nullable: true); + + migrationBuilder.CreateIndex( + name: "ix_user_email", + table: "user", + column: "email", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "ix_user_email", + table: "user"); + + migrationBuilder.DropColumn( + name: "email", + table: "user"); + } + } +} diff --git a/Net.Vatprc.Uniapi/Migrations/VATPRCContextModelSnapshot.cs b/Net.Vatprc.Uniapi/Migrations/VATPRCContextModelSnapshot.cs index accaaee..5b7c3dd 100644 --- a/Net.Vatprc.Uniapi/Migrations/VATPRCContextModelSnapshot.cs +++ b/Net.Vatprc.Uniapi/Migrations/VATPRCContextModelSnapshot.cs @@ -345,6 +345,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnName("created_at") .HasDefaultValueSql("CURRENT_TIMESTAMP"); + b.Property("Email") + .HasColumnType("text") + .HasColumnName("email"); + b.Property("FullName") .IsRequired() .HasColumnType("text") @@ -368,6 +372,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsUnique() .HasDatabaseName("ix_user_cid"); + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("ix_user_email"); + b.ToTable("user", (string)null); }); diff --git a/Net.Vatprc.Uniapi/Models/User.cs b/Net.Vatprc.Uniapi/Models/User.cs index 76cd309..6c441e2 100644 --- a/Net.Vatprc.Uniapi/Models/User.cs +++ b/Net.Vatprc.Uniapi/Models/User.cs @@ -14,6 +14,8 @@ public class User public string FullName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public DateTimeOffset CreatedAt { get; set; } public DateTimeOffset UpdatedAt { get; set; } @@ -29,6 +31,12 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(x => x.Cid) .IsUnique(); + builder.Property(x => x.Email) + .IsRequired(false); + + builder.HasIndex(x => x.Email) + .IsUnique(); + builder.Property(x => x.CreatedAt) .HasDefaultValueSql("CURRENT_TIMESTAMP"); diff --git a/Net.Vatprc.Uniapi/Services/VatsimAuthService.cs b/Net.Vatprc.Uniapi/Services/VatsimAuthService.cs index 2740854..329817c 100644 --- a/Net.Vatprc.Uniapi/Services/VatsimAuthService.cs +++ b/Net.Vatprc.Uniapi/Services/VatsimAuthService.cs @@ -88,6 +88,9 @@ public class DataObject [JsonPropertyName("oauth")] public required OauthObject Oauth { get; set; } + + [JsonPropertyName("personal")] + public required PersonalObject Personal { get; set; } } public class OauthObject @@ -95,6 +98,21 @@ public class OauthObject [JsonPropertyName("token_valid")] public required string TokenValid { get; set; } } + + public class PersonalObject + { + [JsonPropertyName("name_first")] + public required string FirstName { get; set; } + + [JsonPropertyName("name_last")] + public required string LastName { get; set; } + + [JsonPropertyName("name_full")] + public required string FullName { get; set; } + + [JsonPropertyName("email")] + public required string Email { get; set; } + } } public async Task GetUserAsync(string accessToken)