diff --git a/DysonNetwork.Develop/AppDatabase.cs b/DysonNetwork.Develop/AppDatabase.cs index c1b9cc2..07ce308 100644 --- a/DysonNetwork.Develop/AppDatabase.cs +++ b/DysonNetwork.Develop/AppDatabase.cs @@ -1,5 +1,6 @@ using DysonNetwork.Develop.Identity; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; namespace DysonNetwork.Develop; @@ -31,3 +32,17 @@ public class AppDatabase( base.OnModelCreating(modelBuilder); } } + +public class AppDatabaseFactory : IDesignTimeDbContextFactory +{ + public AppDatabase CreateDbContext(string[] args) + { + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + var optionsBuilder = new DbContextOptionsBuilder(); + return new AppDatabase(optionsBuilder.Options, configuration); + } +} diff --git a/DysonNetwork.Develop/Identity/CustomApp.cs b/DysonNetwork.Develop/Identity/CustomApp.cs index a5a84b3..59cab28 100644 --- a/DysonNetwork.Develop/Identity/CustomApp.cs +++ b/DysonNetwork.Develop/Identity/CustomApp.cs @@ -2,8 +2,17 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; using DysonNetwork.Shared.Data; +using DysonNetwork.Shared.Proto; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using NodaTime.Serialization.Protobuf; using NodaTime; +using DysonNetwork.Shared.Proto; +using Google.Protobuf.WellKnownTypes; +using NodaTime.Serialization.Protobuf; +using VerificationMark = DysonNetwork.Shared.Data.VerificationMark; + namespace DysonNetwork.Develop.Identity; public enum CustomAppStatus @@ -25,7 +34,7 @@ public class CustomApp : ModelBase, IIdentifiedResource [Column(TypeName = "jsonb")] public CloudFileReferenceObject? Picture { get; set; } [Column(TypeName = "jsonb")] public CloudFileReferenceObject? Background { get; set; } - [Column(TypeName = "jsonb")] public VerificationMark? Verification { get; set; } + [Column(TypeName = "jsonb")] public DysonNetwork.Shared.Data.VerificationMark? Verification { get; set; } [Column(TypeName = "jsonb")] public CustomAppOauthConfig? OauthConfig { get; set; } [Column(TypeName = "jsonb")] public CustomAppLinks? Links { get; set; } @@ -35,6 +44,66 @@ public class CustomApp : ModelBase, IIdentifiedResource public Developer Developer { get; set; } = null!; [NotMapped] public string ResourceIdentifier => "custom-app:" + Id; + + public Shared.Proto.CustomApp ToProto() + { + return new Shared.Proto.CustomApp + { + Id = Id.ToString(), + Slug = Slug, + Name = Name, + Description = Description ?? string.Empty, + Status = Status switch + { + CustomAppStatus.Developing => Shared.Proto.CustomAppStatus.Developing, + CustomAppStatus.Staging => Shared.Proto.CustomAppStatus.Staging, + CustomAppStatus.Production => Shared.Proto.CustomAppStatus.Production, + CustomAppStatus.Suspended => Shared.Proto.CustomAppStatus.Suspended, + _ => Shared.Proto.CustomAppStatus.Unspecified + }, + Picture = Picture is null ? ByteString.Empty : ByteString.CopyFromUtf8(System.Text.Json.JsonSerializer.Serialize(Picture)), + Background = Background is null ? ByteString.Empty : ByteString.CopyFromUtf8(System.Text.Json.JsonSerializer.Serialize(Background)), + Verification = Verification is null ? ByteString.Empty : ByteString.CopyFromUtf8(System.Text.Json.JsonSerializer.Serialize(Verification)), + Links = Links is null ? ByteString.Empty : ByteString.CopyFromUtf8(System.Text.Json.JsonSerializer.Serialize(Links)), + OauthConfig = OauthConfig is null ? null : new DysonNetwork.Shared.Proto.CustomAppOauthConfig + { + ClientUri = OauthConfig.ClientUri ?? string.Empty, + RedirectUris = { OauthConfig.RedirectUris ?? Array.Empty() }, + PostLogoutRedirectUris = { OauthConfig.PostLogoutRedirectUris ?? Array.Empty() }, + AllowedScopes = { OauthConfig.AllowedScopes ?? Array.Empty() }, + AllowedGrantTypes = { OauthConfig.AllowedGrantTypes ?? Array.Empty() }, + RequirePkce = OauthConfig.RequirePkce, + AllowOfflineAccess = OauthConfig.AllowOfflineAccess + }, + DeveloperId = DeveloperId.ToString(), + CreatedAt = CreatedAt.ToTimestamp(), + UpdatedAt = UpdatedAt.ToTimestamp() + }; + } + + public CustomApp FromProtoValue(Shared.Proto.CustomApp p) + { + Id = Guid.Parse(p.Id); + Slug = p.Slug; + Name = p.Name; + Description = string.IsNullOrEmpty(p.Description) ? null : p.Description; + Status = p.Status switch + { + Shared.Proto.CustomAppStatus.Developing => CustomAppStatus.Developing, + Shared.Proto.CustomAppStatus.Staging => CustomAppStatus.Staging, + Shared.Proto.CustomAppStatus.Production => CustomAppStatus.Production, + Shared.Proto.CustomAppStatus.Suspended => CustomAppStatus.Suspended, + _ => CustomAppStatus.Developing + }; + DeveloperId = string.IsNullOrEmpty(p.DeveloperId) ? Guid.Empty : Guid.Parse(p.DeveloperId); + CreatedAt = p.CreatedAt.ToInstant(); + UpdatedAt = p.UpdatedAt.ToInstant(); + if (p.Picture.Length > 0) Picture = System.Text.Json.JsonSerializer.Deserialize(p.Picture.ToStringUtf8()); + if (p.Background.Length > 0) Background = System.Text.Json.JsonSerializer.Deserialize(p.Background.ToStringUtf8()); + if (p.Verification.Length > 0) Verification = System.Text.Json.JsonSerializer.Deserialize(p.Verification.ToStringUtf8()); + if (p.Links.Length > 0) Links = System.Text.Json.JsonSerializer.Deserialize(p.Links.ToStringUtf8()); + return this; + } } public class CustomAppLinks @@ -65,4 +134,31 @@ public class CustomAppSecret : ModelBase public Guid AppId { get; set; } public CustomApp App { get; set; } = null!; + + + public static CustomAppSecret FromProtoValue(DysonNetwork.Shared.Proto.CustomAppSecret p) + { + return new CustomAppSecret + { + Id = Guid.Parse(p.Id), + Secret = p.Secret, + Description = p.Description, + ExpiredAt = p.ExpiredAt?.ToInstant(), + IsOidc = p.IsOidc, + AppId = Guid.Parse(p.AppId), + }; + } + + public DysonNetwork.Shared.Proto.CustomAppSecret ToProto() + { + return new DysonNetwork.Shared.Proto.CustomAppSecret + { + Id = Id.ToString(), + Secret = Secret, + Description = Description, + ExpiredAt = ExpiredAt?.ToTimestamp(), + IsOidc = IsOidc, + AppId = Id.ToString(), + }; + } } \ No newline at end of file diff --git a/DysonNetwork.Develop/Identity/CustomAppServiceGrpc.cs b/DysonNetwork.Develop/Identity/CustomAppServiceGrpc.cs new file mode 100644 index 0000000..bbe1561 --- /dev/null +++ b/DysonNetwork.Develop/Identity/CustomAppServiceGrpc.cs @@ -0,0 +1,68 @@ +using DysonNetwork.Shared.Proto; +using Grpc.Core; +using Microsoft.EntityFrameworkCore; + +namespace DysonNetwork.Develop.Identity; + +public class CustomAppServiceGrpc(AppDatabase db) : Shared.Proto.CustomAppService.CustomAppServiceBase +{ + public override async Task GetCustomApp(GetCustomAppRequest request, ServerCallContext context) + { + var q = db.CustomApps.AsQueryable(); + switch (request.QueryCase) + { + case GetCustomAppRequest.QueryOneofCase.Id when !string.IsNullOrWhiteSpace(request.Id): + { + if (!Guid.TryParse(request.Id, out var id)) + throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid id")); + var appById = await q.FirstOrDefaultAsync(a => a.Id == id); + if (appById is null) + throw new RpcException(new Status(StatusCode.NotFound, "app not found")); + return new GetCustomAppResponse { App = appById.ToProto() }; + } + case GetCustomAppRequest.QueryOneofCase.Slug when !string.IsNullOrWhiteSpace(request.Slug): + { + var appBySlug = await q.FirstOrDefaultAsync(a => a.Slug == request.Slug); + if (appBySlug is null) + throw new RpcException(new Status(StatusCode.NotFound, "app not found")); + return new GetCustomAppResponse { App = appBySlug.ToProto() }; + } + default: + throw new RpcException(new Status(StatusCode.InvalidArgument, "id or slug required")); + } + } + + public override async Task CheckCustomAppSecret(CheckCustomAppSecretRequest request, ServerCallContext context) + { + if (string.IsNullOrEmpty(request.Secret)) + throw new RpcException(new Status(StatusCode.InvalidArgument, "secret required")); + + IQueryable q = db.CustomAppSecrets; + switch (request.SecretIdentifierCase) + { + case CheckCustomAppSecretRequest.SecretIdentifierOneofCase.SecretId: + { + if (!Guid.TryParse(request.SecretId, out var sid)) + throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid secret_id")); + q = q.Where(s => s.Id == sid); + break; + } + case CheckCustomAppSecretRequest.SecretIdentifierOneofCase.AppId: + { + if (!Guid.TryParse(request.AppId, out var aid)) + throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid app_id")); + q = q.Where(s => s.AppId == aid); + break; + } + default: + throw new RpcException(new Status(StatusCode.InvalidArgument, "secret_id or app_id required")); + } + + if (request.HasIsOidc) + q = q.Where(s => s.IsOidc == request.IsOidc); + + var now = NodaTime.SystemClock.Instance.GetCurrentInstant(); + var exists = await q.AnyAsync(s => s.Secret == request.Secret && (s.ExpiredAt == null || s.ExpiredAt > now)); + return new CheckCustomAppSecretResponse { Valid = exists }; + } +} diff --git a/DysonNetwork.Develop/Migrations/20250807133702_InitialMigration.Designer.cs b/DysonNetwork.Develop/Migrations/20250807133702_InitialMigration.Designer.cs new file mode 100644 index 0000000..b181d32 --- /dev/null +++ b/DysonNetwork.Develop/Migrations/20250807133702_InitialMigration.Designer.cs @@ -0,0 +1,203 @@ +// +using System; +using DysonNetwork.Develop; +using DysonNetwork.Develop.Identity; +using DysonNetwork.Shared.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DysonNetwork.Develop.Migrations +{ + [DbContext(typeof(AppDatabase))] + [Migration("20250807133702_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Background") + .HasColumnType("jsonb") + .HasColumnName("background"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("DeveloperId") + .HasColumnType("uuid") + .HasColumnName("developer_id"); + + b.Property("Links") + .HasColumnType("jsonb") + .HasColumnName("links"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("OauthConfig") + .HasColumnType("jsonb") + .HasColumnName("oauth_config"); + + b.Property("Picture") + .HasColumnType("jsonb") + .HasColumnName("picture"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("slug"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Verification") + .HasColumnType("jsonb") + .HasColumnName("verification"); + + b.HasKey("Id") + .HasName("pk_custom_apps"); + + b.HasIndex("DeveloperId") + .HasDatabaseName("ix_custom_apps_developer_id"); + + b.ToTable("custom_apps", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomAppSecret", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AppId") + .HasColumnType("uuid") + .HasColumnName("app_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("IsOidc") + .HasColumnType("boolean") + .HasColumnName("is_oidc"); + + b.Property("Secret") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("secret"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_custom_app_secrets"); + + b.HasIndex("AppId") + .HasDatabaseName("ix_custom_app_secrets_app_id"); + + b.ToTable("custom_app_secrets", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.Developer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.HasKey("Id") + .HasName("pk_developers"); + + b.ToTable("developers", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b => + { + b.HasOne("DysonNetwork.Develop.Identity.Developer", "Developer") + .WithMany() + .HasForeignKey("DeveloperId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_custom_apps_developers_developer_id"); + + b.Navigation("Developer"); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomAppSecret", b => + { + b.HasOne("DysonNetwork.Develop.Identity.CustomApp", "App") + .WithMany("Secrets") + .HasForeignKey("AppId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_custom_app_secrets_custom_apps_app_id"); + + b.Navigation("App"); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b => + { + b.Navigation("Secrets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DysonNetwork.Develop/Migrations/20250807133702_InitialMigration.cs b/DysonNetwork.Develop/Migrations/20250807133702_InitialMigration.cs new file mode 100644 index 0000000..0714885 --- /dev/null +++ b/DysonNetwork.Develop/Migrations/20250807133702_InitialMigration.cs @@ -0,0 +1,108 @@ +using System; +using DysonNetwork.Develop.Identity; +using DysonNetwork.Shared.Data; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace DysonNetwork.Develop.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "developers", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + publisher_id = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_developers", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "custom_apps", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + slug = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + name = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + status = table.Column(type: "integer", nullable: false), + picture = table.Column(type: "jsonb", nullable: true), + background = table.Column(type: "jsonb", nullable: true), + verification = table.Column(type: "jsonb", nullable: true), + oauth_config = table.Column(type: "jsonb", nullable: true), + links = table.Column(type: "jsonb", nullable: true), + developer_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_custom_apps", x => x.id); + table.ForeignKey( + name: "fk_custom_apps_developers_developer_id", + column: x => x.developer_id, + principalTable: "developers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "custom_app_secrets", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + secret = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + is_oidc = table.Column(type: "boolean", nullable: false), + app_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_custom_app_secrets", x => x.id); + table.ForeignKey( + name: "fk_custom_app_secrets_custom_apps_app_id", + column: x => x.app_id, + principalTable: "custom_apps", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_custom_app_secrets_app_id", + table: "custom_app_secrets", + column: "app_id"); + + migrationBuilder.CreateIndex( + name: "ix_custom_apps_developer_id", + table: "custom_apps", + column: "developer_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "custom_app_secrets"); + + migrationBuilder.DropTable( + name: "custom_apps"); + + migrationBuilder.DropTable( + name: "developers"); + } + } +} diff --git a/DysonNetwork.Develop/Migrations/AppDatabaseModelSnapshot.cs b/DysonNetwork.Develop/Migrations/AppDatabaseModelSnapshot.cs new file mode 100644 index 0000000..9105098 --- /dev/null +++ b/DysonNetwork.Develop/Migrations/AppDatabaseModelSnapshot.cs @@ -0,0 +1,200 @@ +// +using System; +using DysonNetwork.Develop; +using DysonNetwork.Develop.Identity; +using DysonNetwork.Shared.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DysonNetwork.Develop.Migrations +{ + [DbContext(typeof(AppDatabase))] + partial class AppDatabaseModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Background") + .HasColumnType("jsonb") + .HasColumnName("background"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("DeveloperId") + .HasColumnType("uuid") + .HasColumnName("developer_id"); + + b.Property("Links") + .HasColumnType("jsonb") + .HasColumnName("links"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("OauthConfig") + .HasColumnType("jsonb") + .HasColumnName("oauth_config"); + + b.Property("Picture") + .HasColumnType("jsonb") + .HasColumnName("picture"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("slug"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Verification") + .HasColumnType("jsonb") + .HasColumnName("verification"); + + b.HasKey("Id") + .HasName("pk_custom_apps"); + + b.HasIndex("DeveloperId") + .HasDatabaseName("ix_custom_apps_developer_id"); + + b.ToTable("custom_apps", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomAppSecret", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AppId") + .HasColumnType("uuid") + .HasColumnName("app_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("IsOidc") + .HasColumnType("boolean") + .HasColumnName("is_oidc"); + + b.Property("Secret") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("secret"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_custom_app_secrets"); + + b.HasIndex("AppId") + .HasDatabaseName("ix_custom_app_secrets_app_id"); + + b.ToTable("custom_app_secrets", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.Developer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.HasKey("Id") + .HasName("pk_developers"); + + b.ToTable("developers", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b => + { + b.HasOne("DysonNetwork.Develop.Identity.Developer", "Developer") + .WithMany() + .HasForeignKey("DeveloperId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_custom_apps_developers_developer_id"); + + b.Navigation("Developer"); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomAppSecret", b => + { + b.HasOne("DysonNetwork.Develop.Identity.CustomApp", "App") + .WithMany("Secrets") + .HasForeignKey("AppId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_custom_app_secrets_custom_apps_app_id"); + + b.Navigation("App"); + }); + + modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b => + { + b.Navigation("Secrets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DysonNetwork.Develop/Program.cs b/DysonNetwork.Develop/Program.cs index 53f486c..9a32dac 100644 --- a/DysonNetwork.Develop/Program.cs +++ b/DysonNetwork.Develop/Program.cs @@ -15,6 +15,7 @@ builder.Services.AddAppAuthentication(); builder.Services.AddAppSwagger(); builder.Services.AddDysonAuth(); builder.Services.AddPublisherService(); +builder.Services.AddDriveService(); var app = builder.Build(); diff --git a/DysonNetwork.Develop/Startup/ApplicationConfiguration.cs b/DysonNetwork.Develop/Startup/ApplicationConfiguration.cs index b189d3c..6ba95fd 100644 --- a/DysonNetwork.Develop/Startup/ApplicationConfiguration.cs +++ b/DysonNetwork.Develop/Startup/ApplicationConfiguration.cs @@ -1,4 +1,5 @@ using System.Net; +using DysonNetwork.Develop.Identity; using DysonNetwork.Shared.Auth; using Microsoft.AspNetCore.HttpOverrides; using Prometheus; @@ -24,6 +25,8 @@ public static class ApplicationConfiguration app.UseMiddleware(); app.MapControllers(); + + app.MapGrpcService(); return app; } diff --git a/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs index 17defbb..a1041f5 100644 --- a/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs @@ -5,6 +5,7 @@ using NodaTime.Serialization.SystemTextJson; using System.Text.Json; using DysonNetwork.Develop.Identity; using DysonNetwork.Shared.Cache; +using StackExchange.Redis; namespace DysonNetwork.Develop.Startup; @@ -17,6 +18,11 @@ public static class ServiceCollectionExtensions services.AddDbContext(); services.AddSingleton(SystemClock.Instance); services.AddHttpContextAccessor(); + services.AddSingleton(_ => + { + var connection = configuration.GetConnectionString("FastRetrieve")!; + return ConnectionMultiplexer.Connect(connection); + }); services.AddSingleton(); services.AddHttpClient(); diff --git a/DysonNetwork.Pass/Account/Punishment.cs b/DysonNetwork.Pass/Account/Punishment.cs new file mode 100644 index 0000000..e92b27c --- /dev/null +++ b/DysonNetwork.Pass/Account/Punishment.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using DysonNetwork.Shared.Data; +using NodaTime; + +namespace DysonNetwork.Pass.Account; + +public enum PunishmentType +{ + PermissionModification, + BlockLogin, + DisableAccount +} + +public class Punishment : ModelBase +{ + public Guid Id { get; set; } = Guid.NewGuid(); + [MaxLength(8192)] public string Reason { get; set; } = string.Empty; + public Instant? ExpiredAt { get; set; } + + public PunishmentType Type { get; set; } + [Column(TypeName = "jsonb")] public List? BlockedPermissions { get; set; } + + public Guid AccountId { get; set; } + public Account Account { get; set; } = null!; +} \ No newline at end of file diff --git a/DysonNetwork.Pass/AppDatabase.cs b/DysonNetwork.Pass/AppDatabase.cs index e29b2a3..99c3d8c 100644 --- a/DysonNetwork.Pass/AppDatabase.cs +++ b/DysonNetwork.Pass/AppDatabase.cs @@ -2,7 +2,6 @@ using System.Linq.Expressions; using System.Reflection; using DysonNetwork.Pass.Account; using DysonNetwork.Pass.Auth; -using DysonNetwork.Pass.Developer; using DysonNetwork.Pass.Permission; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Data; @@ -45,9 +44,8 @@ public class AppDatabase( public DbSet PaymentTransactions { get; set; } public DbSet WalletSubscriptions { get; set; } public DbSet WalletCoupons { get; set; } - - public DbSet CustomApps { get; set; } - public DbSet CustomAppSecrets { get; set; } + + public DbSet Punishments { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs b/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs index e96a007..6c1605a 100644 --- a/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs +++ b/DysonNetwork.Pass/Auth/OidcProvider/Controllers/OidcProviderController.cs @@ -67,7 +67,7 @@ public class OidcProviderController( // Find the session and related data var session = await oidcService.FindSessionByIdAsync(sessionId); var now = SystemClock.Instance.GetCurrentInstant(); - if (session?.App is null || session.ExpiredAt < now) + if (session?.AppId is null || session.ExpiredAt < now) { return BadRequest(new ErrorResponse { @@ -77,7 +77,7 @@ public class OidcProviderController( } // Get the client - var client = session.App; + var client = await oidcService.FindClientByIdAsync(session.AppId.Value); if (client == null) { return BadRequest(new ErrorResponse diff --git a/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs b/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs index 2ddb88d..f135968 100644 --- a/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs +++ b/DysonNetwork.Pass/Auth/OidcProvider/Services/OidcProviderService.cs @@ -5,8 +5,8 @@ using System.Text; using DysonNetwork.Pass.Auth.OidcProvider.Models; using DysonNetwork.Pass.Auth.OidcProvider.Options; using DysonNetwork.Pass.Auth.OidcProvider.Responses; -using DysonNetwork.Pass.Developer; using DysonNetwork.Shared.Cache; +using DysonNetwork.Shared.Proto; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; @@ -17,6 +17,7 @@ namespace DysonNetwork.Pass.Auth.OidcProvider.Services; public class OidcProviderService( AppDatabase db, AuthService auth, + CustomAppService.CustomAppServiceClient customApps, ICacheService cache, IOptions options, ILogger logger @@ -26,16 +27,8 @@ public class OidcProviderService( public async Task FindClientByIdAsync(Guid clientId) { - return await db.CustomApps - .Include(c => c.Secrets) - .FirstOrDefaultAsync(c => c.Id == clientId); - } - - public async Task FindClientByAppIdAsync(Guid appId) - { - return await db.CustomApps - .Include(c => c.Secrets) - .FirstOrDefaultAsync(c => c.Id == appId); + var resp = await customApps.GetCustomAppAsync(new GetCustomAppRequest { Id = clientId.ToString() }); + return resp.App ?? null; } public async Task FindValidSessionAsync(Guid accountId, Guid clientId) @@ -54,15 +47,13 @@ public class OidcProviderService( public async Task ValidateClientCredentialsAsync(Guid clientId, string clientSecret) { - var client = await FindClientByIdAsync(clientId); - if (client == null) return false; - - var clock = SystemClock.Instance; - var secret = client.Secrets - .Where(s => s.IsOidc && (s.ExpiredAt == null || s.ExpiredAt > clock.GetCurrentInstant())) - .FirstOrDefault(s => s.Secret == clientSecret); // In production, use proper hashing - - return secret != null; + var resp = await customApps.CheckCustomAppSecretAsync(new CheckCustomAppSecretRequest + { + AppId = clientId.ToString(), + Secret = clientSecret, + IsOidc = true + }); + return resp.Valid; } public async Task GenerateTokenResponseAsync( @@ -90,7 +81,7 @@ public class OidcProviderService( var account = await db.Accounts.Where(a => a.Id == authCode.AccountId).FirstOrDefaultAsync(); if (account is null) throw new InvalidOperationException("Account was not found"); - session = await auth.CreateSessionForOidcAsync(account, now, client.Id); + session = await auth.CreateSessionForOidcAsync(account, now, clientId); scopes = authCode.Scopes; } else if (sessionId.HasValue) @@ -143,11 +134,11 @@ public class OidcProviderService( new Claim(JwtRegisteredClaimNames.Jti, session.Id.ToString()), new Claim(JwtRegisteredClaimNames.Iat, now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), - new Claim("client_id", client.Id.ToString()) + new Claim("client_id", client.Id) ]), Expires = expiresAt.ToDateTimeUtc(), Issuer = _options.IssuerUri, - Audience = client.Id.ToString() + Audience = client.Id }; // Try to use RSA signing if keys are available, fall back to HMAC @@ -205,7 +196,6 @@ public class OidcProviderService( return await db.AuthSessions .Include(s => s.Account) .Include(s => s.Challenge) - .Include(s => s.App) .FirstOrDefaultAsync(s => s.Id == sessionId); } diff --git a/DysonNetwork.Pass/Auth/Session.cs b/DysonNetwork.Pass/Auth/Session.cs index 94d3bd8..35e69f4 100644 --- a/DysonNetwork.Pass/Auth/Session.cs +++ b/DysonNetwork.Pass/Auth/Session.cs @@ -1,8 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; -using DysonNetwork.Pass; -using DysonNetwork.Pass.Developer; using DysonNetwork.Shared.Data; using NodaTime; using NodaTime.Serialization.Protobuf; @@ -22,7 +20,6 @@ public class AuthSession : ModelBase public Guid ChallengeId { get; set; } public AuthChallenge Challenge { get; set; } = null!; public Guid? AppId { get; set; } - public CustomApp? App { get; set; } public Shared.Proto.AuthSession ToProtoValue() => new() { diff --git a/DysonNetwork.Pass/Developer/CustomApp.cs b/DysonNetwork.Pass/Developer/CustomApp.cs deleted file mode 100644 index 2910dac..0000000 --- a/DysonNetwork.Pass/Developer/CustomApp.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text.Json.Serialization; -using DysonNetwork.Pass.Account; -using DysonNetwork.Shared.Data; -using NodaTime; - -namespace DysonNetwork.Pass.Developer; - -public enum CustomAppStatus -{ - Developing, - Staging, - Production, - Suspended -} - -public class CustomApp : ModelBase, IIdentifiedResource -{ - public Guid Id { get; set; } = Guid.NewGuid(); - [MaxLength(1024)] public string Slug { get; set; } = null!; - [MaxLength(1024)] public string Name { get; set; } = null!; - [MaxLength(4096)] public string? Description { get; set; } - public CustomAppStatus Status { get; set; } = CustomAppStatus.Developing; - - [Column(TypeName = "jsonb")] public CloudFileReferenceObject? Picture { get; set; } - [Column(TypeName = "jsonb")] public CloudFileReferenceObject? Background { get; set; } - - [Column(TypeName = "jsonb")] public VerificationMark? Verification { get; set; } - [Column(TypeName = "jsonb")] public CustomAppOauthConfig? OauthConfig { get; set; } - [Column(TypeName = "jsonb")] public CustomAppLinks? Links { get; set; } - - [JsonIgnore] public ICollection Secrets { get; set; } = new List(); - - // TODO: Publisher - - [NotMapped] public string ResourceIdentifier => "custom-app:" + Id; -} - -public class CustomAppLinks -{ - [MaxLength(8192)] public string? HomePage { get; set; } - [MaxLength(8192)] public string? PrivacyPolicy { get; set; } - [MaxLength(8192)] public string? TermsOfService { get; set; } -} - -public class CustomAppOauthConfig -{ - [MaxLength(1024)] public string? ClientUri { get; set; } - [MaxLength(4096)] public string[] RedirectUris { get; set; } = []; - [MaxLength(4096)] public string[]? PostLogoutRedirectUris { get; set; } - [MaxLength(256)] public string[]? AllowedScopes { get; set; } = ["openid", "profile", "email"]; - [MaxLength(256)] public string[] AllowedGrantTypes { get; set; } = ["authorization_code", "refresh_token"]; - public bool RequirePkce { get; set; } = true; - public bool AllowOfflineAccess { get; set; } = false; -} - -public class CustomAppSecret : ModelBase -{ - public Guid Id { get; set; } = Guid.NewGuid(); - [MaxLength(1024)] public string Secret { get; set; } = null!; - [MaxLength(4096)] public string? Description { get; set; } = null!; - public Instant? ExpiredAt { get; set; } - public bool IsOidc { get; set; } = false; // Indicates if this secret is for OIDC/OAuth - - public Guid AppId { get; set; } - public CustomApp App { get; set; } = null!; -} diff --git a/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.Designer.cs b/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.Designer.cs index 21c1ca2..cfc7f8f 100644 --- a/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.Designer.cs +++ b/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.Designer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Text.Json; using DysonNetwork.Pass; using DysonNetwork.Pass.Account; -using DysonNetwork.Pass.Developer; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Data; using Microsoft.EntityFrameworkCore; @@ -1047,20 +1046,12 @@ namespace DysonNetwork.Pass.Migrations .HasColumnType("character varying(4096)") .HasColumnName("description"); - b.Property("Links") - .HasColumnType("jsonb") - .HasColumnName("links"); - b.Property("Name") .IsRequired() .HasMaxLength(1024) .HasColumnType("character varying(1024)") .HasColumnName("name"); - b.Property("OauthConfig") - .HasColumnType("jsonb") - .HasColumnName("oauth_config"); - b.Property("Picture") .HasColumnType("jsonb") .HasColumnName("picture"); diff --git a/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.cs b/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.cs index 493dcc9..767e246 100644 --- a/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.cs +++ b/DysonNetwork.Pass/Migrations/20250713121237_InitialMigration.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; using DysonNetwork.Pass.Account; -using DysonNetwork.Pass.Developer; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Data; using Microsoft.EntityFrameworkCore.Migrations; @@ -53,8 +52,6 @@ namespace DysonNetwork.Pass.Migrations picture = table.Column(type: "jsonb", nullable: true), background = table.Column(type: "jsonb", nullable: true), verification = table.Column(type: "jsonb", nullable: true), - oauth_config = table.Column(type: "jsonb", nullable: true), - links = table.Column(type: "jsonb", nullable: true), created_at = table.Column(type: "timestamp with time zone", nullable: false), updated_at = table.Column(type: "timestamp with time zone", nullable: false), deleted_at = table.Column(type: "timestamp with time zone", nullable: true) diff --git a/DysonNetwork.Pass/Migrations/20250715075623_ReinitalMigration.Designer.cs b/DysonNetwork.Pass/Migrations/20250715075623_ReinitalMigration.Designer.cs index 847168a..21c66af 100644 --- a/DysonNetwork.Pass/Migrations/20250715075623_ReinitalMigration.Designer.cs +++ b/DysonNetwork.Pass/Migrations/20250715075623_ReinitalMigration.Designer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Text.Json; using DysonNetwork.Pass; using DysonNetwork.Pass.Account; -using DysonNetwork.Pass.Developer; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Data; using Microsoft.EntityFrameworkCore; @@ -1037,20 +1036,12 @@ namespace DysonNetwork.Pass.Migrations .HasColumnType("character varying(4096)") .HasColumnName("description"); - b.Property("Links") - .HasColumnType("jsonb") - .HasColumnName("links"); - b.Property("Name") .IsRequired() .HasMaxLength(1024) .HasColumnType("character varying(1024)") .HasColumnName("name"); - b.Property("OauthConfig") - .HasColumnType("jsonb") - .HasColumnName("oauth_config"); - b.Property("Picture") .HasColumnType("jsonb") .HasColumnName("picture"); diff --git a/DysonNetwork.Pass/Migrations/20250720193202_RemoveNotification.Designer.cs b/DysonNetwork.Pass/Migrations/20250720193202_RemoveNotification.Designer.cs index fd603ef..9fbeddb 100644 --- a/DysonNetwork.Pass/Migrations/20250720193202_RemoveNotification.Designer.cs +++ b/DysonNetwork.Pass/Migrations/20250720193202_RemoveNotification.Designer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Text.Json; using DysonNetwork.Pass; using DysonNetwork.Pass.Account; -using DysonNetwork.Pass.Developer; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Data; using Microsoft.EntityFrameworkCore; @@ -916,20 +915,12 @@ namespace DysonNetwork.Pass.Migrations .HasColumnType("character varying(4096)") .HasColumnName("description"); - b.Property("Links") - .HasColumnType("jsonb") - .HasColumnName("links"); - b.Property("Name") .IsRequired() .HasMaxLength(1024) .HasColumnType("character varying(1024)") .HasColumnName("name"); - b.Property("OauthConfig") - .HasColumnType("jsonb") - .HasColumnName("oauth_config"); - b.Property("Picture") .HasColumnType("jsonb") .HasColumnName("picture"); diff --git a/DysonNetwork.Pass/Migrations/20250731071524_AddCheckInBackdated.Designer.cs b/DysonNetwork.Pass/Migrations/20250731071524_AddCheckInBackdated.Designer.cs index 8e6843c..6733916 100644 --- a/DysonNetwork.Pass/Migrations/20250731071524_AddCheckInBackdated.Designer.cs +++ b/DysonNetwork.Pass/Migrations/20250731071524_AddCheckInBackdated.Designer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Text.Json; using DysonNetwork.Pass; using DysonNetwork.Pass.Account; -using DysonNetwork.Pass.Developer; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Data; using Microsoft.EntityFrameworkCore; @@ -920,20 +919,12 @@ namespace DysonNetwork.Pass.Migrations .HasColumnType("character varying(4096)") .HasColumnName("description"); - b.Property("Links") - .HasColumnType("jsonb") - .HasColumnName("links"); - b.Property("Name") .IsRequired() .HasMaxLength(1024) .HasColumnType("character varying(1024)") .HasColumnName("name"); - b.Property("OauthConfig") - .HasColumnType("jsonb") - .HasColumnName("oauth_config"); - b.Property("Picture") .HasColumnType("jsonb") .HasColumnName("picture"); diff --git a/DysonNetwork.Pass/Migrations/20250807162919_RemoveDevelopers.Designer.cs b/DysonNetwork.Pass/Migrations/20250807162919_RemoveDevelopers.Designer.cs new file mode 100644 index 0000000..7a99d2c --- /dev/null +++ b/DysonNetwork.Pass/Migrations/20250807162919_RemoveDevelopers.Designer.cs @@ -0,0 +1,1743 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using DysonNetwork.Pass; +using DysonNetwork.Pass.Account; +using DysonNetwork.Pass.Wallet; +using DysonNetwork.Shared.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DysonNetwork.Pass.Migrations +{ + [DbContext(typeof(AppDatabase))] + [Migration("20250807162919_RemoveDevelopers")] + partial class RemoveDevelopers + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AbuseReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("reason"); + + b.Property("Resolution") + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("resolution"); + + b.Property("ResolvedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("resolved_at"); + + b.Property("ResourceIdentifier") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("resource_identifier"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_abuse_reports"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_abuse_reports_account_id"); + + b.ToTable("abuse_reports", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ActivatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("activated_at"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("IsSuperuser") + .HasColumnType("boolean") + .HasColumnName("is_superuser"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("language"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("name"); + + b.Property("Nick") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("nick"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_accounts"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_accounts_name"); + + b.ToTable("accounts", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountAuthFactor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property>("Config") + .HasColumnType("jsonb") + .HasColumnName("config"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("EnabledAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("enabled_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Secret") + .HasMaxLength(8196) + .HasColumnType("character varying(8196)") + .HasColumnName("secret"); + + b.Property("Trustworthy") + .HasColumnType("integer") + .HasColumnName("trustworthy"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_account_auth_factors"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_account_auth_factors_account_id"); + + b.ToTable("account_auth_factors", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountBadge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("ActivatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("activated_at"); + + b.Property("Caption") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("caption"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Label") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("label"); + + b.Property>("Meta") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_badges"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_badges_account_id"); + + b.ToTable("badges", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccessToken") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("access_token"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("LastUsedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_used_at"); + + b.Property>("Meta") + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("ProvidedIdentifier") + .IsRequired() + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("provided_identifier"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("provider"); + + b.Property("RefreshToken") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("refresh_token"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_account_connections"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_account_connections_account_id"); + + b.ToTable("account_connections", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("content"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("IsPrimary") + .HasColumnType("boolean") + .HasColumnName("is_primary"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("VerifiedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("verified_at"); + + b.HasKey("Id") + .HasName("pk_account_contacts"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_account_contacts_account_id"); + + b.ToTable("account_contacts", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("ActiveBadge") + .HasColumnType("jsonb") + .HasColumnName("active_badge"); + + b.Property("Background") + .HasColumnType("jsonb") + .HasColumnName("background"); + + b.Property("Bio") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("bio"); + + b.Property("Birthday") + .HasColumnType("timestamp with time zone") + .HasColumnName("birthday"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Experience") + .HasColumnType("integer") + .HasColumnName("experience"); + + b.Property("FirstName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("first_name"); + + b.Property("Gender") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("gender"); + + b.Property("LastName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("last_name"); + + b.Property("LastSeenAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_at"); + + b.Property("Location") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("location"); + + b.Property("MiddleName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("middle_name"); + + b.Property("Picture") + .HasColumnType("jsonb") + .HasColumnName("picture"); + + b.Property("Pronouns") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("pronouns"); + + b.Property("TimeZone") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("time_zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Verification") + .HasColumnType("jsonb") + .HasColumnName("verification"); + + b.HasKey("Id") + .HasName("pk_account_profiles"); + + b.HasIndex("AccountId") + .IsUnique() + .HasDatabaseName("ix_account_profiles_account_id"); + + b.ToTable("account_profiles", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.ActionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("action"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("IpAddress") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ip_address"); + + b.Property("Location") + .HasColumnType("geometry") + .HasColumnName("location"); + + b.Property>("Meta") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("SessionId") + .HasColumnType("uuid") + .HasColumnName("session_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UserAgent") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("user_agent"); + + b.HasKey("Id") + .HasName("pk_action_logs"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_action_logs_account_id"); + + b.ToTable("action_logs", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.CheckInResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("BackdatedFrom") + .HasColumnType("timestamp with time zone") + .HasColumnName("backdated_from"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Level") + .HasColumnType("integer") + .HasColumnName("level"); + + b.Property("RewardExperience") + .HasColumnType("integer") + .HasColumnName("reward_experience"); + + b.Property("RewardPoints") + .HasColumnType("numeric") + .HasColumnName("reward_points"); + + b.Property>("Tips") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("tips"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_account_check_in_results"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_account_check_in_results_account_id"); + + b.ToTable("account_check_in_results", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.MagicSpell", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("AffectedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("affected_at"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expires_at"); + + b.Property>("Meta") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("Spell") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("spell"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_magic_spells"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_magic_spells_account_id"); + + b.HasIndex("Spell") + .IsUnique() + .HasDatabaseName("ix_magic_spells_spell"); + + b.ToTable("magic_spells", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Punishment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property>("BlockedPermissions") + .HasColumnType("jsonb") + .HasColumnName("blocked_permissions"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("reason"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_punishments"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_punishments_account_id"); + + b.ToTable("punishments", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Relationship", b => + { + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("RelatedId") + .HasColumnType("uuid") + .HasColumnName("related_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Status") + .HasColumnType("smallint") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("AccountId", "RelatedId") + .HasName("pk_account_relationships"); + + b.HasIndex("RelatedId") + .HasDatabaseName("ix_account_relationships_related_id"); + + b.ToTable("account_relationships", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Status", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("Attitude") + .HasColumnType("integer") + .HasColumnName("attitude"); + + b.Property("ClearedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("cleared_at"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("IsInvisible") + .HasColumnType("boolean") + .HasColumnName("is_invisible"); + + b.Property("IsNotDisturb") + .HasColumnType("boolean") + .HasColumnName("is_not_disturb"); + + b.Property("Label") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("label"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_account_statuses"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_account_statuses_account_id"); + + b.ToTable("account_statuses", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Auth.AuthChallenge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property>("Audiences") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("audiences"); + + b.Property>("BlacklistFactors") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("blacklist_factors"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeviceId") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("device_id"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("FailedAttempts") + .HasColumnType("integer") + .HasColumnName("failed_attempts"); + + b.Property("IpAddress") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ip_address"); + + b.Property("Location") + .HasColumnType("geometry") + .HasColumnName("location"); + + b.Property("Nonce") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("nonce"); + + b.Property("Platform") + .HasColumnType("integer") + .HasColumnName("platform"); + + b.Property>("Scopes") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("scopes"); + + b.Property("StepRemain") + .HasColumnType("integer") + .HasColumnName("step_remain"); + + b.Property("StepTotal") + .HasColumnType("integer") + .HasColumnName("step_total"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UserAgent") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("user_agent"); + + b.HasKey("Id") + .HasName("pk_auth_challenges"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_auth_challenges_account_id"); + + b.ToTable("auth_challenges", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Auth.AuthSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("AppId") + .HasColumnType("uuid") + .HasColumnName("app_id"); + + b.Property("ChallengeId") + .HasColumnType("uuid") + .HasColumnName("challenge_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Label") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("label"); + + b.Property("LastGrantedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_granted_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_auth_sessions"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_auth_sessions_account_id"); + + b.HasIndex("ChallengeId") + .HasDatabaseName("ix_auth_sessions_challenge_id"); + + b.ToTable("auth_sessions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("key"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_permission_groups"); + + b.ToTable("permission_groups", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionGroupMember", b => + { + b.Property("GroupId") + .HasColumnType("uuid") + .HasColumnName("group_id"); + + b.Property("Actor") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("actor"); + + b.Property("AffectedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("affected_at"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("GroupId", "Actor") + .HasName("pk_permission_group_members"); + + b.ToTable("permission_group_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("actor"); + + b.Property("AffectedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("affected_at"); + + b.Property("Area") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("area"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("GroupId") + .HasColumnType("uuid") + .HasColumnName("group_id"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("key"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Value") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("value"); + + b.HasKey("Id") + .HasName("pk_permission_nodes"); + + b.HasIndex("GroupId") + .HasDatabaseName("ix_permission_nodes_group_id"); + + b.HasIndex("Key", "Area", "Actor") + .HasDatabaseName("ix_permission_nodes_key_area_actor"); + + b.ToTable("permission_nodes", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Coupon", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AffectedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("affected_at"); + + b.Property("Code") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("code"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DiscountAmount") + .HasColumnType("numeric") + .HasColumnName("discount_amount"); + + b.Property("DiscountRate") + .HasColumnType("double precision") + .HasColumnName("discount_rate"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Identifier") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("identifier"); + + b.Property("MaxUsage") + .HasColumnType("integer") + .HasColumnName("max_usage"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_wallet_coupons"); + + b.ToTable("wallet_coupons", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Amount") + .HasColumnType("numeric") + .HasColumnName("amount"); + + b.Property("AppIdentifier") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("app_identifier"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("currency"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property>("Meta") + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("PayeeWalletId") + .HasColumnType("uuid") + .HasColumnName("payee_wallet_id"); + + b.Property("Remarks") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("remarks"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("TransactionId") + .HasColumnType("uuid") + .HasColumnName("transaction_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_payment_orders"); + + b.HasIndex("PayeeWalletId") + .HasDatabaseName("ix_payment_orders_payee_wallet_id"); + + b.HasIndex("TransactionId") + .HasDatabaseName("ix_payment_orders_transaction_id"); + + b.ToTable("payment_orders", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("BasePrice") + .HasColumnType("numeric") + .HasColumnName("base_price"); + + b.Property("BegunAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("begun_at"); + + b.Property("CouponId") + .HasColumnType("uuid") + .HasColumnName("coupon_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("ended_at"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("identifier"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasColumnName("is_active"); + + b.Property("IsFreeTrial") + .HasColumnType("boolean") + .HasColumnName("is_free_trial"); + + b.Property("PaymentDetails") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("payment_details"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("payment_method"); + + b.Property("RenewalAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("renewal_at"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_wallet_subscriptions"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_wallet_subscriptions_account_id"); + + b.HasIndex("CouponId") + .HasDatabaseName("ix_wallet_subscriptions_coupon_id"); + + b.HasIndex("Identifier") + .HasDatabaseName("ix_wallet_subscriptions_identifier"); + + b.ToTable("wallet_subscriptions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Transaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Amount") + .HasColumnType("numeric") + .HasColumnName("amount"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("currency"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("PayeeWalletId") + .HasColumnType("uuid") + .HasColumnName("payee_wallet_id"); + + b.Property("PayerWalletId") + .HasColumnType("uuid") + .HasColumnName("payer_wallet_id"); + + b.Property("Remarks") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("remarks"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_payment_transactions"); + + b.HasIndex("PayeeWalletId") + .HasDatabaseName("ix_payment_transactions_payee_wallet_id"); + + b.HasIndex("PayerWalletId") + .HasDatabaseName("ix_payment_transactions_payer_wallet_id"); + + b.ToTable("payment_transactions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Wallet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_wallets"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_wallets_account_id"); + + b.ToTable("wallets", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.WalletPocket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Amount") + .HasColumnType("numeric") + .HasColumnName("amount"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("currency"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("WalletId") + .HasColumnType("uuid") + .HasColumnName("wallet_id"); + + b.HasKey("Id") + .HasName("pk_wallet_pockets"); + + b.HasIndex("WalletId") + .HasDatabaseName("ix_wallet_pockets_wallet_id"); + + b.ToTable("wallet_pockets", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AbuseReport", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_abuse_reports_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountAuthFactor", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany("AuthFactors") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_auth_factors_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountBadge", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany("Badges") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_badges_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountConnection", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany("Connections") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_connections_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountContact", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany("Contacts") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_contacts_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.AccountProfile", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithOne("Profile") + .HasForeignKey("DysonNetwork.Pass.Account.AccountProfile", "AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_profiles_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.ActionLog", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_action_logs_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.CheckInResult", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_check_in_results_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.MagicSpell", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .HasConstraintName("fk_magic_spells_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Punishment", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_punishments_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Relationship", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany("OutgoingRelationships") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_relationships_accounts_account_id"); + + b.HasOne("DysonNetwork.Pass.Account.Account", "Related") + .WithMany("IncomingRelationships") + .HasForeignKey("RelatedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_relationships_accounts_related_id"); + + b.Navigation("Account"); + + b.Navigation("Related"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Status", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_statuses_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Auth.AuthChallenge", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany("Challenges") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_auth_challenges_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Auth.AuthSession", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany("Sessions") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_auth_sessions_accounts_account_id"); + + b.HasOne("DysonNetwork.Pass.Auth.AuthChallenge", "Challenge") + .WithMany() + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_auth_sessions_auth_challenges_challenge_id"); + + b.Navigation("Account"); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionGroupMember", b => + { + b.HasOne("DysonNetwork.Pass.Permission.PermissionGroup", "Group") + .WithMany("Members") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_permission_group_members_permission_groups_group_id"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionNode", b => + { + b.HasOne("DysonNetwork.Pass.Permission.PermissionGroup", "Group") + .WithMany("Nodes") + .HasForeignKey("GroupId") + .HasConstraintName("fk_permission_nodes_permission_groups_group_id"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Order", b => + { + b.HasOne("DysonNetwork.Pass.Wallet.Wallet", "PayeeWallet") + .WithMany() + .HasForeignKey("PayeeWalletId") + .HasConstraintName("fk_payment_orders_wallets_payee_wallet_id"); + + b.HasOne("DysonNetwork.Pass.Wallet.Transaction", "Transaction") + .WithMany() + .HasForeignKey("TransactionId") + .HasConstraintName("fk_payment_orders_payment_transactions_transaction_id"); + + b.Navigation("PayeeWallet"); + + b.Navigation("Transaction"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Subscription", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_wallet_subscriptions_accounts_account_id"); + + b.HasOne("DysonNetwork.Pass.Wallet.Coupon", "Coupon") + .WithMany() + .HasForeignKey("CouponId") + .HasConstraintName("fk_wallet_subscriptions_wallet_coupons_coupon_id"); + + b.Navigation("Account"); + + b.Navigation("Coupon"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Transaction", b => + { + b.HasOne("DysonNetwork.Pass.Wallet.Wallet", "PayeeWallet") + .WithMany() + .HasForeignKey("PayeeWalletId") + .HasConstraintName("fk_payment_transactions_wallets_payee_wallet_id"); + + b.HasOne("DysonNetwork.Pass.Wallet.Wallet", "PayerWallet") + .WithMany() + .HasForeignKey("PayerWalletId") + .HasConstraintName("fk_payment_transactions_wallets_payer_wallet_id"); + + b.Navigation("PayeeWallet"); + + b.Navigation("PayerWallet"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Wallet", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_wallets_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.WalletPocket", b => + { + b.HasOne("DysonNetwork.Pass.Wallet.Wallet", "Wallet") + .WithMany("Pockets") + .HasForeignKey("WalletId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_wallet_pockets_wallets_wallet_id"); + + b.Navigation("Wallet"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Account.Account", b => + { + b.Navigation("AuthFactors"); + + b.Navigation("Badges"); + + b.Navigation("Challenges"); + + b.Navigation("Connections"); + + b.Navigation("Contacts"); + + b.Navigation("IncomingRelationships"); + + b.Navigation("OutgoingRelationships"); + + b.Navigation("Profile") + .IsRequired(); + + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionGroup", b => + { + b.Navigation("Members"); + + b.Navigation("Nodes"); + }); + + modelBuilder.Entity("DysonNetwork.Pass.Wallet.Wallet", b => + { + b.Navigation("Pockets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DysonNetwork.Pass/Migrations/20250807162919_RemoveDevelopers.cs b/DysonNetwork.Pass/Migrations/20250807162919_RemoveDevelopers.cs new file mode 100644 index 0000000..643a430 --- /dev/null +++ b/DysonNetwork.Pass/Migrations/20250807162919_RemoveDevelopers.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using DysonNetwork.Shared.Data; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace DysonNetwork.Pass.Migrations +{ + /// + public partial class RemoveDevelopers : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "fk_auth_sessions_custom_apps_app_id", + table: "auth_sessions"); + + migrationBuilder.DropTable( + name: "custom_app_secrets"); + + migrationBuilder.DropTable( + name: "custom_apps"); + + migrationBuilder.DropIndex( + name: "ix_auth_sessions_app_id", + table: "auth_sessions"); + + migrationBuilder.CreateTable( + name: "punishments", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + reason = table.Column(type: "character varying(8192)", maxLength: 8192, nullable: false), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + type = table.Column(type: "integer", nullable: false), + blocked_permissions = table.Column>(type: "jsonb", nullable: true), + account_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_punishments", x => x.id); + table.ForeignKey( + name: "fk_punishments_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_punishments_account_id", + table: "punishments", + column: "account_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "punishments"); + + migrationBuilder.CreateTable( + name: "custom_apps", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + background = table.Column(type: "jsonb", nullable: true), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + name = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + picture = table.Column(type: "jsonb", nullable: true), + slug = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + status = table.Column(type: "integer", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false), + verification = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_custom_apps", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "custom_app_secrets", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + app_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + is_oidc = table.Column(type: "boolean", nullable: false), + secret = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_custom_app_secrets", x => x.id); + table.ForeignKey( + name: "fk_custom_app_secrets_custom_apps_app_id", + column: x => x.app_id, + principalTable: "custom_apps", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_auth_sessions_app_id", + table: "auth_sessions", + column: "app_id"); + + migrationBuilder.CreateIndex( + name: "ix_custom_app_secrets_app_id", + table: "custom_app_secrets", + column: "app_id"); + + migrationBuilder.AddForeignKey( + name: "fk_auth_sessions_custom_apps_app_id", + table: "auth_sessions", + column: "app_id", + principalTable: "custom_apps", + principalColumn: "id"); + } + } +} diff --git a/DysonNetwork.Pass/Migrations/AppDatabaseModelSnapshot.cs b/DysonNetwork.Pass/Migrations/AppDatabaseModelSnapshot.cs index 33a2b2d..46bc52b 100644 --- a/DysonNetwork.Pass/Migrations/AppDatabaseModelSnapshot.cs +++ b/DysonNetwork.Pass/Migrations/AppDatabaseModelSnapshot.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Text.Json; using DysonNetwork.Pass; using DysonNetwork.Pass.Account; -using DysonNetwork.Pass.Developer; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Data; using Microsoft.EntityFrameworkCore; @@ -647,6 +646,56 @@ namespace DysonNetwork.Pass.Migrations b.ToTable("magic_spells", (string)null); }); + modelBuilder.Entity("DysonNetwork.Pass.Account.Punishment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property>("BlockedPermissions") + .HasColumnType("jsonb") + .HasColumnName("blocked_permissions"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("reason"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_punishments"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_punishments_account_id"); + + b.ToTable("punishments", (string)null); + }); + modelBuilder.Entity("DysonNetwork.Pass.Account.Relationship", b => { b.Property("AccountId") @@ -884,132 +933,12 @@ namespace DysonNetwork.Pass.Migrations b.HasIndex("AccountId") .HasDatabaseName("ix_auth_sessions_account_id"); - b.HasIndex("AppId") - .HasDatabaseName("ix_auth_sessions_app_id"); - b.HasIndex("ChallengeId") .HasDatabaseName("ix_auth_sessions_challenge_id"); b.ToTable("auth_sessions", (string)null); }); - modelBuilder.Entity("DysonNetwork.Pass.Developer.CustomApp", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Background") - .HasColumnType("jsonb") - .HasColumnName("background"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("deleted_at"); - - b.Property("Description") - .HasMaxLength(4096) - .HasColumnType("character varying(4096)") - .HasColumnName("description"); - - b.Property("Links") - .HasColumnType("jsonb") - .HasColumnName("links"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("name"); - - b.Property("OauthConfig") - .HasColumnType("jsonb") - .HasColumnName("oauth_config"); - - b.Property("Picture") - .HasColumnType("jsonb") - .HasColumnName("picture"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.Property("Verification") - .HasColumnType("jsonb") - .HasColumnName("verification"); - - b.HasKey("Id") - .HasName("pk_custom_apps"); - - b.ToTable("custom_apps", (string)null); - }); - - modelBuilder.Entity("DysonNetwork.Pass.Developer.CustomAppSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AppId") - .HasColumnType("uuid") - .HasColumnName("app_id"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("deleted_at"); - - b.Property("Description") - .HasMaxLength(4096) - .HasColumnType("character varying(4096)") - .HasColumnName("description"); - - b.Property("ExpiredAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expired_at"); - - b.Property("IsOidc") - .HasColumnType("boolean") - .HasColumnName("is_oidc"); - - b.Property("Secret") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("secret"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("Id") - .HasName("pk_custom_app_secrets"); - - b.HasIndex("AppId") - .HasDatabaseName("ix_custom_app_secrets_app_id"); - - b.ToTable("custom_app_secrets", (string)null); - }); - modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionGroup", b => { b.Property("Id") @@ -1595,6 +1524,18 @@ namespace DysonNetwork.Pass.Migrations b.Navigation("Account"); }); + modelBuilder.Entity("DysonNetwork.Pass.Account.Punishment", b => + { + b.HasOne("DysonNetwork.Pass.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_punishments_accounts_account_id"); + + b.Navigation("Account"); + }); + modelBuilder.Entity("DysonNetwork.Pass.Account.Relationship", b => { b.HasOne("DysonNetwork.Pass.Account.Account", "Account") @@ -1649,11 +1590,6 @@ namespace DysonNetwork.Pass.Migrations .IsRequired() .HasConstraintName("fk_auth_sessions_accounts_account_id"); - b.HasOne("DysonNetwork.Pass.Developer.CustomApp", "App") - .WithMany() - .HasForeignKey("AppId") - .HasConstraintName("fk_auth_sessions_custom_apps_app_id"); - b.HasOne("DysonNetwork.Pass.Auth.AuthChallenge", "Challenge") .WithMany() .HasForeignKey("ChallengeId") @@ -1663,23 +1599,9 @@ namespace DysonNetwork.Pass.Migrations b.Navigation("Account"); - b.Navigation("App"); - b.Navigation("Challenge"); }); - modelBuilder.Entity("DysonNetwork.Pass.Developer.CustomAppSecret", b => - { - b.HasOne("DysonNetwork.Pass.Developer.CustomApp", "App") - .WithMany("Secrets") - .HasForeignKey("AppId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_custom_app_secrets_custom_apps_app_id"); - - b.Navigation("App"); - }); - modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionGroupMember", b => { b.HasOne("DysonNetwork.Pass.Permission.PermissionGroup", "Group") @@ -1801,11 +1723,6 @@ namespace DysonNetwork.Pass.Migrations b.Navigation("Sessions"); }); - modelBuilder.Entity("DysonNetwork.Pass.Developer.CustomApp", b => - { - b.Navigation("Secrets"); - }); - modelBuilder.Entity("DysonNetwork.Pass.Permission.PermissionGroup", b => { b.Navigation("Members"); diff --git a/DysonNetwork.Pass/Program.cs b/DysonNetwork.Pass/Program.cs index 32cdc11..15619dd 100644 --- a/DysonNetwork.Pass/Program.cs +++ b/DysonNetwork.Pass/Program.cs @@ -22,6 +22,7 @@ builder.Services.AddAppAuthentication(); builder.Services.AddAppSwagger(); builder.Services.AddPusherService(); builder.Services.AddDriveService(); +builder.Services.AddDevelopService(); // Add flush handlers and websocket handlers builder.Services.AddAppFlushHandlers(); diff --git a/DysonNetwork.Shared/Proto/GrpcClientHelper.cs b/DysonNetwork.Shared/Proto/GrpcClientHelper.cs index c562fa5..11589cf 100644 --- a/DysonNetwork.Shared/Proto/GrpcClientHelper.cs +++ b/DysonNetwork.Shared/Proto/GrpcClientHelper.cs @@ -135,4 +135,16 @@ public static class GrpcClientHelper return new PublisherService.PublisherServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, clientCertPassword)); } + + public static async Task CreateCustomAppServiceClient( + IEtcdClient etcdClient, + string clientCertPath, + string clientKeyPath, + string? clientCertPassword = null + ) + { + var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Develop"); + return new CustomAppService.CustomAppServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath, + clientCertPassword)); + } } \ No newline at end of file diff --git a/DysonNetwork.Shared/Proto/develop.proto b/DysonNetwork.Shared/Proto/develop.proto new file mode 100644 index 0000000..85be2b4 --- /dev/null +++ b/DysonNetwork.Shared/Proto/develop.proto @@ -0,0 +1,88 @@ +syntax = "proto3"; + +package proto; + +option csharp_namespace = "DysonNetwork.Shared.Proto"; + +import "google/protobuf/timestamp.proto"; +import "file.proto"; + +message CustomAppOauthConfig { + string client_uri = 1; + repeated string redirect_uris = 2; + repeated string post_logout_redirect_uris = 3; + repeated string allowed_scopes = 4; + repeated string allowed_grant_types = 5; + bool require_pkce = 6; + bool allow_offline_access = 7; +} + +enum CustomAppStatus { + CUSTOM_APP_STATUS_UNSPECIFIED = 0; + DEVELOPING = 1; + STAGING = 2; + PRODUCTION = 3; + SUSPENDED = 4; +} + + message CustomApp { + string id = 1; + string slug = 2; + string name = 3; + string description = 4; + CustomAppStatus status = 5; + + // jsonb columns represented as bytes + bytes picture = 6; + bytes background = 7; + bytes verification = 8; + bytes links = 9; + CustomAppOauthConfig oauth_config = 13; + + string developer_id = 10; + + google.protobuf.Timestamp created_at = 11; + google.protobuf.Timestamp updated_at = 12; + } + +message CustomAppSecret { + string id = 1; + string secret = 2; + string description = 3; + google.protobuf.Timestamp expired_at = 4; + bool is_oidc = 5; + + string app_id = 6; + + google.protobuf.Timestamp created_at = 7; + google.protobuf.Timestamp updated_at = 8; +} + +message GetCustomAppRequest { + oneof Query { + string id = 1; + string slug = 2; + } +} + +message GetCustomAppResponse { + CustomApp app = 1; +} + +message CheckCustomAppSecretRequest { + oneof SecretIdentifier { + string secret_id = 1; + string app_id = 2; + } + string secret = 3; + optional bool is_oidc = 4; +} + +message CheckCustomAppSecretResponse { + bool valid = 1; +} + +service CustomAppService { + rpc GetCustomApp(GetCustomAppRequest) returns (GetCustomAppResponse); + rpc CheckCustomAppSecret(CheckCustomAppSecretRequest) returns (CheckCustomAppSecretResponse); +} \ No newline at end of file diff --git a/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs b/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs index 985b762..8d56332 100644 --- a/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs +++ b/DysonNetwork.Shared/Registry/ServiceInjectionHelper.cs @@ -111,4 +111,23 @@ public static class ServiceInjectionHelper return services; } -} \ No newline at end of file + + public static IServiceCollection AddDevelopService(this IServiceCollection services) + { + services.AddSingleton(sp => + { + var etcdClient = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + var clientCertPath = config["Service:ClientCert"]!; + var clientKeyPath = config["Service:ClientKey"]!; + var clientCertPassword = config["Service:CertPassword"]; + + return GrpcClientHelper + .CreateCustomAppServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword) + .GetAwaiter() + .GetResult(); + }); + + return services; + } + } \ No newline at end of file diff --git a/DysonNetwork.Sphere/Activity/Activity.cs b/DysonNetwork.Sphere/Activity/Activity.cs index 41f6498..ab602a0 100644 --- a/DysonNetwork.Sphere/Activity/Activity.cs +++ b/DysonNetwork.Sphere/Activity/Activity.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using DysonNetwork.Shared.Data; using NodaTime; namespace DysonNetwork.Sphere.Activity; diff --git a/DysonNetwork.Sphere/Activity/ActivityService.cs b/DysonNetwork.Sphere/Activity/ActivityService.cs index 103eb29..2d067bf 100644 --- a/DysonNetwork.Sphere/Activity/ActivityService.cs +++ b/DysonNetwork.Sphere/Activity/ActivityService.cs @@ -36,7 +36,7 @@ public class ActivityService( if (cursor == null && (debugInclude.Contains("realms") || Random.Shared.NextDouble() < 0.2)) { - var realms = await ds.GetPublicRealmsAsync(null, null, 5, 0, true); + var realms = await ds.GetPublicRealmsAsync(null, 5, 0, true); if (realms.Count > 0) { activities.Add(new DiscoveryActivity( @@ -142,7 +142,7 @@ public class ActivityService( { if (cursor == null && (debugInclude.Contains("realms") || Random.Shared.NextDouble() < 0.2)) { - var realms = await ds.GetPublicRealmsAsync(null, null, 5, 0, true); + var realms = await ds.GetPublicRealmsAsync(null, 5, 0, true); if (realms.Count > 0) { activities.Add(new DiscoveryActivity( diff --git a/DysonNetwork.Sphere/AppDatabase.cs b/DysonNetwork.Sphere/AppDatabase.cs index dc97f94..ef9d7a3 100644 --- a/DysonNetwork.Sphere/AppDatabase.cs +++ b/DysonNetwork.Sphere/AppDatabase.cs @@ -1,5 +1,6 @@ using System.Linq.Expressions; using System.Reflection; +using DysonNetwork.Shared.Data; using DysonNetwork.Sphere.Chat; using DysonNetwork.Sphere.Post; using DysonNetwork.Sphere.Publisher; @@ -18,13 +19,6 @@ public interface IIdentifiedResource public string ResourceIdentifier { get; } } -public abstract class ModelBase -{ - public Instant CreatedAt { get; set; } - public Instant UpdatedAt { get; set; } - public Instant? DeletedAt { get; set; } -} - public class AppDatabase( DbContextOptions options, IConfiguration configuration @@ -47,8 +41,6 @@ public class AppDatabase( public DbSet Realms { get; set; } public DbSet RealmMembers { get; set; } - public DbSet Tags { get; set; } - public DbSet RealmTags { get; set; } public DbSet ChatRooms { get; set; } public DbSet ChatMembers { get; set; } @@ -130,19 +122,6 @@ public class AppDatabase( .HasForeignKey(pm => pm.RealmId) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasKey(rt => new { rt.RealmId, rt.TagId }); - modelBuilder.Entity() - .HasOne(rt => rt.Realm) - .WithMany(r => r.RealmTags) - .HasForeignKey(rt => rt.RealmId) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasOne(rt => rt.Tag) - .WithMany(t => t.RealmTags) - .HasForeignKey(rt => rt.TagId) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() .HasKey(pm => new { pm.Id }); modelBuilder.Entity() diff --git a/DysonNetwork.Sphere/Chat/RealtimeCall.cs b/DysonNetwork.Sphere/Chat/RealtimeCall.cs index d363a09..eaa5042 100644 --- a/DysonNetwork.Sphere/Chat/RealtimeCall.cs +++ b/DysonNetwork.Sphere/Chat/RealtimeCall.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json; using System.Text.Json.Serialization; +using DysonNetwork.Shared.Data; using DysonNetwork.Sphere.Chat.Realtime; using NodaTime; diff --git a/DysonNetwork.Sphere/Discovery/DiscoveryController.cs b/DysonNetwork.Sphere/Discovery/DiscoveryController.cs index db450d4..acb88b8 100644 --- a/DysonNetwork.Sphere/Discovery/DiscoveryController.cs +++ b/DysonNetwork.Sphere/Discovery/DiscoveryController.cs @@ -9,11 +9,10 @@ public class DiscoveryController(DiscoveryService discoveryService) : Controller [HttpGet("realms")] public Task> GetPublicRealms( [FromQuery] string? query, - [FromQuery] List? tags, [FromQuery] int take = 10, [FromQuery] int offset = 0 ) { - return discoveryService.GetPublicRealmsAsync(query, tags, take, offset); + return discoveryService.GetPublicRealmsAsync(query, take, offset); } } diff --git a/DysonNetwork.Sphere/Discovery/DiscoveryService.cs b/DysonNetwork.Sphere/Discovery/DiscoveryService.cs index 29b8aec..2286d76 100644 --- a/DysonNetwork.Sphere/Discovery/DiscoveryService.cs +++ b/DysonNetwork.Sphere/Discovery/DiscoveryService.cs @@ -4,8 +4,8 @@ namespace DysonNetwork.Sphere.Discovery; public class DiscoveryService(AppDatabase appDatabase) { - public Task> GetPublicRealmsAsync(string? query, - List? tags, + public Task> GetPublicRealmsAsync( + string? query, int take = 10, int offset = 0, bool randomizer = false @@ -21,8 +21,6 @@ public class DiscoveryService(AppDatabase appDatabase) EF.Functions.ILike(r.Name, $"%{query}%") || EF.Functions.ILike(r.Description, $"%{query}%") ); - if (tags is { Count: > 0 }) - realmsQuery = realmsQuery.Where(r => r.RealmTags.Any(rt => tags.Contains(rt.Tag.Name))); if (randomizer) realmsQuery = realmsQuery.OrderBy(r => EF.Functions.Random()); else diff --git a/DysonNetwork.Sphere/Migrations/20250807162646_RemoveDevelopers.Designer.cs b/DysonNetwork.Sphere/Migrations/20250807162646_RemoveDevelopers.Designer.cs new file mode 100644 index 0000000..b676dc0 --- /dev/null +++ b/DysonNetwork.Sphere/Migrations/20250807162646_RemoveDevelopers.Designer.cs @@ -0,0 +1,1878 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using DysonNetwork.Shared.Data; +using DysonNetwork.Sphere; +using DysonNetwork.Sphere.Chat; +using DysonNetwork.Sphere.Poll; +using DysonNetwork.Sphere.WebReader; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace DysonNetwork.Sphere.Migrations +{ + [DbContext(typeof(AppDatabase))] + [Migration("20250807162646_RemoveDevelopers")] + partial class RemoveDevelopers + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("BreakUntil") + .HasColumnType("timestamp with time zone") + .HasColumnName("break_until"); + + b.Property("ChatRoomId") + .HasColumnType("uuid") + .HasColumnName("chat_room_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("IsBot") + .HasColumnType("boolean") + .HasColumnName("is_bot"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("joined_at"); + + b.Property("LastReadAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_read_at"); + + b.Property("LeaveAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("leave_at"); + + b.Property("Nick") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("nick"); + + b.Property("Notify") + .HasColumnType("integer") + .HasColumnName("notify"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.Property("TimeoutCause") + .HasColumnType("jsonb") + .HasColumnName("timeout_cause"); + + b.Property("TimeoutUntil") + .HasColumnType("timestamp with time zone") + .HasColumnName("timeout_until"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_chat_members"); + + b.HasAlternateKey("ChatRoomId", "AccountId") + .HasName("ak_chat_members_chat_room_id_account_id"); + + b.ToTable("chat_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Background") + .HasColumnType("jsonb") + .HasColumnName("background"); + + b.Property("BackgroundId") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("background_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("IsCommunity") + .HasColumnType("boolean") + .HasColumnName("is_community"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("is_public"); + + b.Property("Name") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("Picture") + .HasColumnType("jsonb") + .HasColumnName("picture"); + + b.Property("PictureId") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("picture_id"); + + b.Property("RealmId") + .HasColumnType("uuid") + .HasColumnName("realm_id"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_chat_rooms"); + + b.HasIndex("RealmId") + .HasDatabaseName("ix_chat_rooms_realm_id"); + + b.ToTable("chat_rooms", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property>("Attachments") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("attachments"); + + b.Property("ChatRoomId") + .HasColumnType("uuid") + .HasColumnName("chat_room_id"); + + b.Property("Content") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("content"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("EditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("edited_at"); + + b.Property("ForwardedMessageId") + .HasColumnType("uuid") + .HasColumnName("forwarded_message_id"); + + b.Property>("MembersMentioned") + .HasColumnType("jsonb") + .HasColumnName("members_mentioned"); + + b.Property>("Meta") + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("Nonce") + .IsRequired() + .HasMaxLength(36) + .HasColumnType("character varying(36)") + .HasColumnName("nonce"); + + b.Property("RepliedMessageId") + .HasColumnType("uuid") + .HasColumnName("replied_message_id"); + + b.Property("SenderId") + .HasColumnType("uuid") + .HasColumnName("sender_id"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_chat_messages"); + + b.HasIndex("ChatRoomId") + .HasDatabaseName("ix_chat_messages_chat_room_id"); + + b.HasIndex("ForwardedMessageId") + .HasDatabaseName("ix_chat_messages_forwarded_message_id"); + + b.HasIndex("RepliedMessageId") + .HasDatabaseName("ix_chat_messages_replied_message_id"); + + b.HasIndex("SenderId") + .HasDatabaseName("ix_chat_messages_sender_id"); + + b.ToTable("chat_messages", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Attitude") + .HasColumnType("integer") + .HasColumnName("attitude"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("MessageId") + .HasColumnType("uuid") + .HasColumnName("message_id"); + + b.Property("SenderId") + .HasColumnType("uuid") + .HasColumnName("sender_id"); + + b.Property("Symbol") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("symbol"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_chat_reactions"); + + b.HasIndex("MessageId") + .HasDatabaseName("ix_chat_reactions_message_id"); + + b.HasIndex("SenderId") + .HasDatabaseName("ix_chat_reactions_sender_id"); + + b.ToTable("chat_reactions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.RealtimeCall", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("ended_at"); + + b.Property("ProviderName") + .HasColumnType("text") + .HasColumnName("provider_name"); + + b.Property("RoomId") + .HasColumnType("uuid") + .HasColumnName("room_id"); + + b.Property("SenderId") + .HasColumnType("uuid") + .HasColumnName("sender_id"); + + b.Property("SessionId") + .HasColumnType("text") + .HasColumnName("session_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UpstreamConfigJson") + .HasColumnType("jsonb") + .HasColumnName("upstream"); + + b.HasKey("Id") + .HasName("pk_chat_realtime_call"); + + b.HasIndex("RoomId") + .HasDatabaseName("ix_chat_realtime_call_room_id"); + + b.HasIndex("SenderId") + .HasDatabaseName("ix_chat_realtime_call_sender_id"); + + b.ToTable("chat_realtime_call", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Poll.Poll", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("ended_at"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("Title") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_polls"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_polls_publisher_id"); + + b.ToTable("polls", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Poll.PollAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property>("Answer") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("answer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("PollId") + .HasColumnType("uuid") + .HasColumnName("poll_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_poll_answers"); + + b.HasIndex("PollId") + .HasDatabaseName("ix_poll_answers_poll_id"); + + b.ToTable("poll_answers", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Poll.PollQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("IsRequired") + .HasColumnType("boolean") + .HasColumnName("is_required"); + + b.Property>("Options") + .HasColumnType("jsonb") + .HasColumnName("options"); + + b.Property("Order") + .HasColumnType("integer") + .HasColumnName("order"); + + b.Property("PollId") + .HasColumnType("uuid") + .HasColumnName("poll_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_poll_questions"); + + b.HasIndex("PollId") + .HasDatabaseName("ix_poll_questions_poll_id"); + + b.ToTable("poll_questions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property>("Attachments") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("attachments"); + + b.Property("Content") + .HasColumnType("text") + .HasColumnName("content"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("Downvotes") + .HasColumnType("integer") + .HasColumnName("downvotes"); + + b.Property("EditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("edited_at"); + + b.Property("ForwardedPostId") + .HasColumnType("uuid") + .HasColumnName("forwarded_post_id"); + + b.Property("Language") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("language"); + + b.Property>("Meta") + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("PublishedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("published_at"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("RepliedPostId") + .HasColumnType("uuid") + .HasColumnName("replied_post_id"); + + b.Property("SearchVector") + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasColumnName("search_vector") + .HasAnnotation("Npgsql:TsVectorConfig", "simple") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Title", "Description", "Content" }); + + b.Property>("SensitiveMarks") + .HasColumnType("jsonb") + .HasColumnName("sensitive_marks"); + + b.Property("Title") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Upvotes") + .HasColumnType("integer") + .HasColumnName("upvotes"); + + b.Property("ViewsTotal") + .HasColumnType("integer") + .HasColumnName("views_total"); + + b.Property("ViewsUnique") + .HasColumnType("integer") + .HasColumnName("views_unique"); + + b.Property("Visibility") + .HasColumnType("integer") + .HasColumnName("visibility"); + + b.HasKey("Id") + .HasName("pk_posts"); + + b.HasIndex("ForwardedPostId") + .HasDatabaseName("ix_posts_forwarded_post_id"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_posts_publisher_id"); + + b.HasIndex("RepliedPostId") + .HasDatabaseName("ix_posts_replied_post_id"); + + b.HasIndex("SearchVector") + .HasDatabaseName("ix_posts_search_vector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("SearchVector"), "GIN"); + + b.ToTable("posts", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("name"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("slug"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_post_categories"); + + b.ToTable("post_categories", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("name"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("slug"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_post_collections"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_post_collections_publisher_id"); + + b.ToTable("post_collections", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("Attitude") + .HasColumnType("integer") + .HasColumnName("attitude"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("PostId") + .HasColumnType("uuid") + .HasColumnName("post_id"); + + b.Property("Symbol") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("symbol"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_post_reactions"); + + b.HasIndex("PostId") + .HasDatabaseName("ix_post_reactions_post_id"); + + b.ToTable("post_reactions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("name"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("slug"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_post_tags"); + + b.ToTable("post_tags", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.Publisher", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("Background") + .HasColumnType("jsonb") + .HasColumnName("background"); + + b.Property("BackgroundId") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("background_id"); + + b.Property("Bio") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("bio"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("name"); + + b.Property("Nick") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("nick"); + + b.Property("Picture") + .HasColumnType("jsonb") + .HasColumnName("picture"); + + b.Property("PictureId") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("picture_id"); + + b.Property("RealmId") + .HasColumnType("uuid") + .HasColumnName("realm_id"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Verification") + .HasColumnType("jsonb") + .HasColumnName("verification"); + + b.HasKey("Id") + .HasName("pk_publishers"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_publishers_name"); + + b.HasIndex("RealmId") + .HasDatabaseName("ix_publishers_realm_id"); + + b.ToTable("publishers", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.PublisherFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property("Flag") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("flag"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_publisher_features"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_publisher_features_publisher_id"); + + b.ToTable("publisher_features", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.PublisherMember", b => + { + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("joined_at"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("PublisherId", "AccountId") + .HasName("pk_publisher_members"); + + b.ToTable("publisher_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.PublisherSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("Tier") + .HasColumnType("integer") + .HasColumnName("tier"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_publisher_subscriptions"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_publisher_subscriptions_publisher_id"); + + b.ToTable("publisher_subscriptions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("Background") + .HasColumnType("jsonb") + .HasColumnName("background"); + + b.Property("BackgroundId") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("background_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("IsCommunity") + .HasColumnType("boolean") + .HasColumnName("is_community"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("is_public"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("Picture") + .HasColumnType("jsonb") + .HasColumnName("picture"); + + b.Property("PictureId") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("picture_id"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("slug"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Verification") + .HasColumnType("jsonb") + .HasColumnName("verification"); + + b.HasKey("Id") + .HasName("pk_realms"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("ix_realms_slug"); + + b.ToTable("realms", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmMember", b => + { + b.Property("RealmId") + .HasColumnType("uuid") + .HasColumnName("realm_id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("joined_at"); + + b.Property("LeaveAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("leave_at"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("RealmId", "AccountId") + .HasName("pk_realm_members"); + + b.ToTable("realm_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Sticker.Sticker", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Image") + .HasColumnType("jsonb") + .HasColumnName("image"); + + b.Property("ImageId") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("image_id"); + + b.Property("PackId") + .HasColumnType("uuid") + .HasColumnName("pack_id"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("slug"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_stickers"); + + b.HasIndex("PackId") + .HasDatabaseName("ix_stickers_pack_id"); + + b.HasIndex("Slug") + .HasDatabaseName("ix_stickers_slug"); + + b.ToTable("stickers", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("Prefix") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("prefix"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_sticker_packs"); + + b.HasIndex("Prefix") + .IsUnique() + .HasDatabaseName("ix_sticker_packs_prefix"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_sticker_packs_publisher_id"); + + b.ToTable("sticker_packs", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPackOwnership", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("uuid") + .HasColumnName("account_id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("PackId") + .HasColumnType("uuid") + .HasColumnName("pack_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_sticker_pack_ownerships"); + + b.HasIndex("PackId") + .HasDatabaseName("ix_sticker_pack_ownerships_pack_id"); + + b.ToTable("sticker_pack_ownerships", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.WebReader.WebArticle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Author") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("author"); + + b.Property("Content") + .HasColumnType("text") + .HasColumnName("content"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("FeedId") + .HasColumnType("uuid") + .HasColumnName("feed_id"); + + b.Property>("Meta") + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("Preview") + .HasColumnType("jsonb") + .HasColumnName("preview"); + + b.Property("PublishedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("published_at"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("url"); + + b.HasKey("Id") + .HasName("pk_web_articles"); + + b.HasIndex("FeedId") + .HasDatabaseName("ix_web_articles_feed_id"); + + b.HasIndex("Url") + .IsUnique() + .HasDatabaseName("ix_web_articles_url"); + + b.ToTable("web_articles", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.WebReader.WebFeed", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Config") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("config"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("Description") + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("description"); + + b.Property("Preview") + .HasColumnType("jsonb") + .HasColumnName("preview"); + + b.Property("PublisherId") + .HasColumnType("uuid") + .HasColumnName("publisher_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(8192) + .HasColumnType("character varying(8192)") + .HasColumnName("url"); + + b.HasKey("Id") + .HasName("pk_web_feeds"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_web_feeds_publisher_id"); + + b.HasIndex("Url") + .IsUnique() + .HasDatabaseName("ix_web_feeds_url"); + + b.ToTable("web_feeds", (string)null); + }); + + modelBuilder.Entity("PostPostCategory", b => + { + b.Property("CategoriesId") + .HasColumnType("uuid") + .HasColumnName("categories_id"); + + b.Property("PostsId") + .HasColumnType("uuid") + .HasColumnName("posts_id"); + + b.HasKey("CategoriesId", "PostsId") + .HasName("pk_post_category_links"); + + b.HasIndex("PostsId") + .HasDatabaseName("ix_post_category_links_posts_id"); + + b.ToTable("post_category_links", (string)null); + }); + + modelBuilder.Entity("PostPostCollection", b => + { + b.Property("CollectionsId") + .HasColumnType("uuid") + .HasColumnName("collections_id"); + + b.Property("PostsId") + .HasColumnType("uuid") + .HasColumnName("posts_id"); + + b.HasKey("CollectionsId", "PostsId") + .HasName("pk_post_collection_links"); + + b.HasIndex("PostsId") + .HasDatabaseName("ix_post_collection_links_posts_id"); + + b.ToTable("post_collection_links", (string)null); + }); + + modelBuilder.Entity("PostPostTag", b => + { + b.Property("PostsId") + .HasColumnType("uuid") + .HasColumnName("posts_id"); + + b.Property("TagsId") + .HasColumnType("uuid") + .HasColumnName("tags_id"); + + b.HasKey("PostsId", "TagsId") + .HasName("pk_post_tag_links"); + + b.HasIndex("TagsId") + .HasDatabaseName("ix_post_tag_links_tags_id"); + + b.ToTable("post_tag_links", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatMember", b => + { + b.HasOne("DysonNetwork.Sphere.Chat.ChatRoom", "ChatRoom") + .WithMany("Members") + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_members_chat_rooms_chat_room_id"); + + b.Navigation("ChatRoom"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") + .WithMany("ChatRooms") + .HasForeignKey("RealmId") + .HasConstraintName("fk_chat_rooms_realms_realm_id"); + + b.Navigation("Realm"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b => + { + b.HasOne("DysonNetwork.Sphere.Chat.ChatRoom", "ChatRoom") + .WithMany() + .HasForeignKey("ChatRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_messages_chat_rooms_chat_room_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.Message", "ForwardedMessage") + .WithMany() + .HasForeignKey("ForwardedMessageId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_chat_messages_chat_messages_forwarded_message_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.Message", "RepliedMessage") + .WithMany() + .HasForeignKey("RepliedMessageId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_chat_messages_chat_messages_replied_message_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_messages_chat_members_sender_id"); + + b.Navigation("ChatRoom"); + + b.Navigation("ForwardedMessage"); + + b.Navigation("RepliedMessage"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageReaction", b => + { + b.HasOne("DysonNetwork.Sphere.Chat.Message", "Message") + .WithMany("Reactions") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_reactions_chat_messages_message_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_reactions_chat_members_sender_id"); + + b.Navigation("Message"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.RealtimeCall", b => + { + b.HasOne("DysonNetwork.Sphere.Chat.ChatRoom", "Room") + .WithMany() + .HasForeignKey("RoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_realtime_call_chat_rooms_room_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_realtime_call_chat_members_sender_id"); + + b.Navigation("Room"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Poll.Poll", b => + { + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany("Polls") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_polls_publishers_publisher_id"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Poll.PollAnswer", b => + { + b.HasOne("DysonNetwork.Sphere.Poll.Poll", "Poll") + .WithMany() + .HasForeignKey("PollId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_poll_answers_polls_poll_id"); + + b.Navigation("Poll"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Poll.PollQuestion", b => + { + b.HasOne("DysonNetwork.Sphere.Poll.Poll", "Poll") + .WithMany("Questions") + .HasForeignKey("PollId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_poll_questions_polls_poll_id"); + + b.Navigation("Poll"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Post", b => + { + b.HasOne("DysonNetwork.Sphere.Post.Post", "ForwardedPost") + .WithMany() + .HasForeignKey("ForwardedPostId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_posts_posts_forwarded_post_id"); + + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany("Posts") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_posts_publishers_publisher_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Post", "RepliedPost") + .WithMany() + .HasForeignKey("RepliedPostId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_posts_posts_replied_post_id"); + + b.Navigation("ForwardedPost"); + + b.Navigation("Publisher"); + + b.Navigation("RepliedPost"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b => + { + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany("Collections") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_collections_publishers_publisher_id"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b => + { + b.HasOne("DysonNetwork.Sphere.Post.Post", "Post") + .WithMany("Reactions") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_reactions_posts_post_id"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.Publisher", b => + { + b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") + .WithMany() + .HasForeignKey("RealmId") + .HasConstraintName("fk_publishers_realms_realm_id"); + + b.Navigation("Realm"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.PublisherFeature", b => + { + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany("Features") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publisher_features_publishers_publisher_id"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.PublisherMember", b => + { + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany("Members") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publisher_members_publishers_publisher_id"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.PublisherSubscription", b => + { + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany("Subscriptions") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publisher_subscriptions_publishers_publisher_id"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmMember", b => + { + b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") + .WithMany("Members") + .HasForeignKey("RealmId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_realm_members_realms_realm_id"); + + b.Navigation("Realm"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Sticker.Sticker", b => + { + b.HasOne("DysonNetwork.Sphere.Sticker.StickerPack", "Pack") + .WithMany("Stickers") + .HasForeignKey("PackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_stickers_sticker_packs_pack_id"); + + b.Navigation("Pack"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPack", b => + { + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany() + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_sticker_packs_publishers_publisher_id"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPackOwnership", b => + { + b.HasOne("DysonNetwork.Sphere.Sticker.StickerPack", "Pack") + .WithMany() + .HasForeignKey("PackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_sticker_pack_ownerships_sticker_packs_pack_id"); + + b.Navigation("Pack"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.WebReader.WebArticle", b => + { + b.HasOne("DysonNetwork.Sphere.WebReader.WebFeed", "Feed") + .WithMany("Articles") + .HasForeignKey("FeedId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_web_articles_web_feeds_feed_id"); + + b.Navigation("Feed"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.WebReader.WebFeed", b => + { + b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") + .WithMany() + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_web_feeds_publishers_publisher_id"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("PostPostCategory", b => + { + b.HasOne("DysonNetwork.Sphere.Post.PostCategory", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_category_links_post_categories_categories_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Post", null) + .WithMany() + .HasForeignKey("PostsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_category_links_posts_posts_id"); + }); + + modelBuilder.Entity("PostPostCollection", b => + { + b.HasOne("DysonNetwork.Sphere.Post.PostCollection", null) + .WithMany() + .HasForeignKey("CollectionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_collection_links_post_collections_collections_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Post", null) + .WithMany() + .HasForeignKey("PostsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_collection_links_posts_posts_id"); + }); + + modelBuilder.Entity("PostPostTag", b => + { + b.HasOne("DysonNetwork.Sphere.Post.Post", null) + .WithMany() + .HasForeignKey("PostsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_tag_links_posts_posts_id"); + + b.HasOne("DysonNetwork.Sphere.Post.PostTag", null) + .WithMany() + .HasForeignKey("TagsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_tag_links_post_tags_tags_id"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.Navigation("Members"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b => + { + b.Navigation("Reactions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Poll.Poll", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Post", b => + { + b.Navigation("Reactions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Publisher.Publisher", b => + { + b.Navigation("Collections"); + + b.Navigation("Features"); + + b.Navigation("Members"); + + b.Navigation("Polls"); + + b.Navigation("Posts"); + + b.Navigation("Subscriptions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.Navigation("ChatRooms"); + + b.Navigation("Members"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPack", b => + { + b.Navigation("Stickers"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.WebReader.WebFeed", b => + { + b.Navigation("Articles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DysonNetwork.Sphere/Migrations/20250807162646_RemoveDevelopers.cs b/DysonNetwork.Sphere/Migrations/20250807162646_RemoveDevelopers.cs new file mode 100644 index 0000000..eb5679a --- /dev/null +++ b/DysonNetwork.Sphere/Migrations/20250807162646_RemoveDevelopers.cs @@ -0,0 +1,149 @@ +using System; +using DysonNetwork.Shared.Data; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace DysonNetwork.Sphere.Migrations +{ + /// + public partial class RemoveDevelopers : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "custom_app_secrets"); + + migrationBuilder.DropTable( + name: "realm_tags"); + + migrationBuilder.DropTable( + name: "custom_apps"); + + migrationBuilder.DropTable( + name: "tags"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "custom_apps", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + publisher_id = table.Column(type: "uuid", nullable: false), + background = table.Column(type: "jsonb", nullable: true), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + name = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + picture = table.Column(type: "jsonb", nullable: true), + slug = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + status = table.Column(type: "integer", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false), + verification = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_custom_apps", x => x.id); + table.ForeignKey( + name: "fk_custom_apps_publishers_publisher_id", + column: x => x.publisher_id, + principalTable: "publishers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "tags", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true), + name = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_tags", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "custom_app_secrets", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + app_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + is_oidc = table.Column(type: "boolean", nullable: false), + secret = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_custom_app_secrets", x => x.id); + table.ForeignKey( + name: "fk_custom_app_secrets_custom_apps_app_id", + column: x => x.app_id, + principalTable: "custom_apps", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "realm_tags", + columns: table => new + { + realm_id = table.Column(type: "uuid", nullable: false), + tag_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + deleted_at = table.Column(type: "timestamp with time zone", nullable: true), + updated_at = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_realm_tags", x => new { x.realm_id, x.tag_id }); + table.ForeignKey( + name: "fk_realm_tags_realms_realm_id", + column: x => x.realm_id, + principalTable: "realms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_realm_tags_tags_tag_id", + column: x => x.tag_id, + principalTable: "tags", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_custom_app_secrets_app_id", + table: "custom_app_secrets", + column: "app_id"); + + migrationBuilder.CreateIndex( + name: "ix_custom_app_secrets_secret", + table: "custom_app_secrets", + column: "secret", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_custom_apps_publisher_id", + table: "custom_apps", + column: "publisher_id"); + + migrationBuilder.CreateIndex( + name: "ix_realm_tags_tag_id", + table: "realm_tags", + column: "tag_id"); + } + } +} diff --git a/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs b/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs index f6b53eb..77a760c 100644 --- a/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs +++ b/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs @@ -371,126 +371,6 @@ namespace DysonNetwork.Sphere.Migrations b.ToTable("chat_realtime_call", (string)null); }); - modelBuilder.Entity("DysonNetwork.Sphere.Developer.CustomApp", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Background") - .HasColumnType("jsonb") - .HasColumnName("background"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("deleted_at"); - - b.Property("Description") - .HasMaxLength(4096) - .HasColumnType("character varying(4096)") - .HasColumnName("description"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("name"); - - b.Property("Picture") - .HasColumnType("jsonb") - .HasColumnName("picture"); - - b.Property("PublisherId") - .HasColumnType("uuid") - .HasColumnName("publisher_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.Property("Verification") - .HasColumnType("jsonb") - .HasColumnName("verification"); - - b.HasKey("Id") - .HasName("pk_custom_apps"); - - b.HasIndex("PublisherId") - .HasDatabaseName("ix_custom_apps_publisher_id"); - - b.ToTable("custom_apps", (string)null); - }); - - modelBuilder.Entity("DysonNetwork.Sphere.Developer.CustomAppSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AppId") - .HasColumnType("uuid") - .HasColumnName("app_id"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("deleted_at"); - - b.Property("Description") - .HasMaxLength(4096) - .HasColumnType("character varying(4096)") - .HasColumnName("description"); - - b.Property("ExpiredAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("expired_at"); - - b.Property("IsOidc") - .HasColumnType("boolean") - .HasColumnName("is_oidc"); - - b.Property("Secret") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)") - .HasColumnName("secret"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("Id") - .HasName("pk_custom_app_secrets"); - - b.HasIndex("AppId") - .HasDatabaseName("ix_custom_app_secrets_app_id"); - - b.HasIndex("Secret") - .IsUnique() - .HasDatabaseName("ix_custom_app_secrets_secret"); - - b.ToTable("custom_app_secrets", (string)null); - }); - modelBuilder.Entity("DysonNetwork.Sphere.Poll.Poll", b => { b.Property("Id") @@ -1252,68 +1132,6 @@ namespace DysonNetwork.Sphere.Migrations b.ToTable("realm_members", (string)null); }); - modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmTag", b => - { - b.Property("RealmId") - .HasColumnType("uuid") - .HasColumnName("realm_id"); - - b.Property("TagId") - .HasColumnType("uuid") - .HasColumnName("tag_id"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("deleted_at"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("RealmId", "TagId") - .HasName("pk_realm_tags"); - - b.HasIndex("TagId") - .HasDatabaseName("ix_realm_tags_tag_id"); - - b.ToTable("realm_tags", (string)null); - }); - - modelBuilder.Entity("DysonNetwork.Sphere.Realm.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_at"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("deleted_at"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("name"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_at"); - - b.HasKey("Id") - .HasName("pk_tags"); - - b.ToTable("tags", (string)null); - }); - modelBuilder.Entity("DysonNetwork.Sphere.Sticker.Sticker", b => { b.Property("Id") @@ -1743,30 +1561,6 @@ namespace DysonNetwork.Sphere.Migrations b.Navigation("Sender"); }); - modelBuilder.Entity("DysonNetwork.Sphere.Developer.CustomApp", b => - { - b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Developer") - .WithMany() - .HasForeignKey("PublisherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_custom_apps_publishers_publisher_id"); - - b.Navigation("Developer"); - }); - - modelBuilder.Entity("DysonNetwork.Sphere.Developer.CustomAppSecret", b => - { - b.HasOne("DysonNetwork.Sphere.Developer.CustomApp", "App") - .WithMany("Secrets") - .HasForeignKey("AppId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_custom_app_secrets_custom_apps_app_id"); - - b.Navigation("App"); - }); - modelBuilder.Entity("DysonNetwork.Sphere.Poll.Poll", b => { b.HasOne("DysonNetwork.Sphere.Publisher.Publisher", "Publisher") @@ -1913,27 +1707,6 @@ namespace DysonNetwork.Sphere.Migrations b.Navigation("Realm"); }); - modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmTag", b => - { - b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") - .WithMany("RealmTags") - .HasForeignKey("RealmId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_realm_tags_realms_realm_id"); - - b.HasOne("DysonNetwork.Sphere.Realm.Tag", "Tag") - .WithMany("RealmTags") - .HasForeignKey("TagId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_realm_tags_tags_tag_id"); - - b.Navigation("Realm"); - - b.Navigation("Tag"); - }); - modelBuilder.Entity("DysonNetwork.Sphere.Sticker.Sticker", b => { b.HasOne("DysonNetwork.Sphere.Sticker.StickerPack", "Pack") @@ -2055,11 +1828,6 @@ namespace DysonNetwork.Sphere.Migrations b.Navigation("Reactions"); }); - modelBuilder.Entity("DysonNetwork.Sphere.Developer.CustomApp", b => - { - b.Navigation("Secrets"); - }); - modelBuilder.Entity("DysonNetwork.Sphere.Poll.Poll", b => { b.Navigation("Questions"); @@ -2090,13 +1858,6 @@ namespace DysonNetwork.Sphere.Migrations b.Navigation("ChatRooms"); b.Navigation("Members"); - - b.Navigation("RealmTags"); - }); - - modelBuilder.Entity("DysonNetwork.Sphere.Realm.Tag", b => - { - b.Navigation("RealmTags"); }); modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPack", b => diff --git a/DysonNetwork.Sphere/Poll/Poll.cs b/DysonNetwork.Sphere/Poll/Poll.cs index 9b0c1db..c66b267 100644 --- a/DysonNetwork.Sphere/Poll/Poll.cs +++ b/DysonNetwork.Sphere/Poll/Poll.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json; using System.Text.Json.Serialization; +using DysonNetwork.Shared.Data; using NodaTime; namespace DysonNetwork.Sphere.Poll; diff --git a/DysonNetwork.Sphere/Post/PostService.cs b/DysonNetwork.Sphere/Post/PostService.cs index 1842bec..ff30907 100644 --- a/DysonNetwork.Sphere/Post/PostService.cs +++ b/DysonNetwork.Sphere/Post/PostService.cs @@ -688,7 +688,7 @@ public partial class PostService( var pollEmbed = embeds[pollIndex]; try { - var pollId = Guid.Parse(((JsonElement)pollEmbed["Id"]).ToString()); + var pollId = Guid.Parse(((JsonElement)pollEmbed["id"]).ToString()); Guid? currentUserId = currentUser is not null ? Guid.Parse(currentUser.Id) : null; var updatedPoll = await polls.LoadPollEmbed(pollId, currentUserId); diff --git a/DysonNetwork.Sphere/Realm/Realm.cs b/DysonNetwork.Sphere/Realm/Realm.cs index a7306e4..c6b7eb3 100644 --- a/DysonNetwork.Sphere/Realm/Realm.cs +++ b/DysonNetwork.Sphere/Realm/Realm.cs @@ -30,7 +30,6 @@ public class Realm : ModelBase, IIdentifiedResource [JsonIgnore] public ICollection Members { get; set; } = new List(); [JsonIgnore] public ICollection ChatRooms { get; set; } = new List(); - [JsonIgnore] public ICollection RealmTags { get; set; } = new List(); public Guid AccountId { get; set; } diff --git a/DysonNetwork.Sphere/Realm/RealmTag.cs b/DysonNetwork.Sphere/Realm/RealmTag.cs deleted file mode 100644 index 864ba2a..0000000 --- a/DysonNetwork.Sphere/Realm/RealmTag.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace DysonNetwork.Sphere.Realm; - -public class RealmTag : ModelBase -{ - public Guid RealmId { get; set; } - public Realm Realm { get; set; } = null!; - - public Guid TagId { get; set; } - public Tag Tag { get; set; } = null!; -} diff --git a/DysonNetwork.Sphere/Realm/Tag.cs b/DysonNetwork.Sphere/Realm/Tag.cs deleted file mode 100644 index d87e6e4..0000000 --- a/DysonNetwork.Sphere/Realm/Tag.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace DysonNetwork.Sphere.Realm; - -public class Tag : ModelBase -{ - public Guid Id { get; set; } - [MaxLength(64)] - public string Name { get; set; } = string.Empty; - - public ICollection RealmTags { get; set; } = new List(); -} diff --git a/DysonNetwork.Sphere/WebReader/WebArticle.cs b/DysonNetwork.Sphere/WebReader/WebArticle.cs index fb06ed7..45c4dcc 100644 --- a/DysonNetwork.Sphere/WebReader/WebArticle.cs +++ b/DysonNetwork.Sphere/WebReader/WebArticle.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; +using DysonNetwork.Shared.Data; namespace DysonNetwork.Sphere.WebReader; diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index e0ed2bb..e0c41ab 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -121,6 +121,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded