From d0a92bc8b33949ee8e67f13e4baa7948989e3000 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 4 May 2025 14:48:33 +0800 Subject: [PATCH] :recycle: Optimization in file uploading --- DysonNetwork.Sphere/Chat/ChatController.cs | 155 +- DysonNetwork.Sphere/Chat/ChatService.cs | 14 +- .../Connection/WebSocketPacket.cs | 4 +- .../Connection/WebSocketService.cs | 1 - .../DysonNetwork.Sphere.csproj | 3 + ...0250503124624_InitialMigration.Designer.cs | 2359 +++++++++++++++++ .../20250503124624_InitialMigration.cs | 1389 ++++++++++ .../Migrations/AppDatabaseModelSnapshot.cs | 2356 ++++++++++++++++ .../Permission/PermissionService.cs | 66 +- DysonNetwork.Sphere/Post/PostController.cs | 2 - DysonNetwork.Sphere/Program.cs | 59 +- DysonNetwork.Sphere/Storage/FileService.cs | 92 +- DysonNetwork.Sphere/appsettings.json | 2 +- DysonNetwork.sln.DotSettings.user | 8 + 14 files changed, 6436 insertions(+), 74 deletions(-) create mode 100644 DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.Designer.cs create mode 100644 DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.cs create mode 100644 DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs diff --git a/DysonNetwork.Sphere/Chat/ChatController.cs b/DysonNetwork.Sphere/Chat/ChatController.cs index f02be24..5a7cdcc 100644 --- a/DysonNetwork.Sphere/Chat/ChatController.cs +++ b/DysonNetwork.Sphere/Chat/ChatController.cs @@ -1,8 +1,10 @@ using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; +using DysonNetwork.Sphere.Storage; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using SystemClock = NodaTime.SystemClock; namespace DysonNetwork.Sphere.Chat; @@ -22,6 +24,8 @@ public partial class ChatController(AppDatabase db, ChatService cs) : Controller [MaxLength(36)] public string? Nonce { get; set; } public List? AttachmentsId { get; set; } public Dictionary? Meta { get; set; } + public Guid? RepliedMessageId { get; set; } + public Guid? ForwardedMessageId { get; set; } } [HttpGet("{roomId:long}/messages")] @@ -52,8 +56,6 @@ public partial class ChatController(AppDatabase db, ChatService cs) : Controller .Include(m => m.Sender) .Include(m => m.Sender.Account) .Include(m => m.Sender.Account.Profile) - .Include(m => m.Sender.Account.Profile.Picture) - .Include(m => m.Sender.Account.Profile.Background) .Include(m => m.Attachments) .Skip(offset) .Take(take) @@ -63,8 +65,42 @@ public partial class ChatController(AppDatabase db, ChatService cs) : Controller return Ok(messages); } + + [HttpGet("{roomId:long}/messages/{messageId:guid}")] + public async Task> GetMessage(long roomId, Guid messageId) + { + var currentUser = HttpContext.Items["CurrentUser"] as Account.Account; + + var room = await db.ChatRooms.FirstOrDefaultAsync(r => r.Id == roomId); + if (room is null) return NotFound(); + + if (!room.IsPublic) + { + if (currentUser is null) return Unauthorized(); + + var member = await db.ChatMembers + .Where(m => m.AccountId == currentUser.Id && m.ChatRoomId == roomId) + .FirstOrDefaultAsync(); + if (member == null || member.Role < ChatMemberRole.Normal) + return StatusCode(403, "You are not a member of this chat room."); + } + + var message = await db.ChatMessages + .Where(m => m.Id == messageId && m.ChatRoomId == roomId) + .Include(m => m.Sender) + .Include(m => m.Sender.Account) + .Include(m => m.Sender.Account.Profile) + .Include(m => m.Attachments) + .FirstOrDefaultAsync(); + + if (message is null) return NotFound(); + + return Ok(message); + } + + - [GeneratedRegex(@"@([A-Za-z0-9_-]+)")] + [GeneratedRegex("@([A-Za-z0-9_-]+)")] private static partial Regex MentionRegex(); [HttpPost("{roomId:long}/messages")] @@ -102,6 +138,26 @@ public partial class ChatController(AppDatabase db, ChatService cs) : Controller .OrderBy(f => request.AttachmentsId.IndexOf(f.Id)) .ToList(); } + + if (request.RepliedMessageId.HasValue) + { + var repliedMessage = await db.ChatMessages + .FirstOrDefaultAsync(m => m.Id == request.RepliedMessageId.Value && m.ChatRoomId == roomId); + if (repliedMessage == null) + return BadRequest("The message you're replying to does not exist."); + + message.RepliedMessageId = repliedMessage.Id; + } + + if (request.ForwardedMessageId.HasValue) + { + var forwardedMessage = await db.ChatMessages + .FirstOrDefaultAsync(m => m.Id == request.ForwardedMessageId.Value); + if (forwardedMessage == null) + return BadRequest("The message you're forwarding does not exist."); + + message.ForwardedMessageId = forwardedMessage.Id; + } if (request.Content is not null) { @@ -123,14 +179,105 @@ public partial class ChatController(AppDatabase db, ChatService cs) : Controller return Ok(result); } + + + [HttpPatch("{roomId:long}/messages/{messageId:guid}")] + [Authorize] + public async Task UpdateMessage([FromBody] SendMessageRequest request, long roomId, Guid messageId) + { + if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); + + var message = await db.ChatMessages + .Include(m => m.Sender) + .Include(m => m.Sender.Account) + .Include(m => m.Sender.Account.Profile) + .FirstOrDefaultAsync(m => m.Id == messageId && m.ChatRoomId == roomId); + if (message == null) return NotFound(); + + if (message.Sender.AccountId != currentUser.Id) + return StatusCode(403, "You can only edit your own messages."); + + if (request.Content is not null) + message.Content = request.Content; + if (request.Meta is not null) + message.Meta = request.Meta; + + if (request.RepliedMessageId.HasValue) + { + var repliedMessage = await db.ChatMessages + .FirstOrDefaultAsync(m => m.Id == request.RepliedMessageId.Value && m.ChatRoomId == roomId); + if (repliedMessage == null) + return BadRequest("The message you're replying to does not exist."); + + message.RepliedMessageId = repliedMessage.Id; + } + + if (request.ForwardedMessageId.HasValue) + { + var forwardedMessage = await db.ChatMessages + .FirstOrDefaultAsync(m => m.Id == request.ForwardedMessageId.Value); + if (forwardedMessage == null) + return BadRequest("The message you're forwarding does not exist."); + + message.ForwardedMessageId = forwardedMessage.Id; + } + + if (request.AttachmentsId is not null) + { + var records = await db.Files.Where(f => request.AttachmentsId.Contains(f.Id)).ToListAsync(); + + var previous = message.Attachments?.ToDictionary(f => f.Id) ?? new Dictionary(); + var current = records.ToDictionary(f => f.Id); + + // Detect added files + var added = current.Keys.Except(previous.Keys).Select(id => current[id]).ToList(); + // Detect removed files + var removed = previous.Keys.Except(current.Keys).Select(id => previous[id]).ToList(); + + // Update attachments + message.Attachments = request.AttachmentsId.Select(id => current[id]).ToList(); + + // Call mark usage + var fs = HttpContext.RequestServices.GetRequiredService(); + await fs.MarkUsageRangeAsync(added, 1); + await fs.MarkUsageRangeAsync(removed, -1); + } + + message.EditedAt = SystemClock.Instance.GetCurrentInstant(); + db.Update(message); + await db.SaveChangesAsync(); + + return Ok(message); + } + + [HttpDelete("{roomId:long}/messages/{messageId:guid}")] + [Authorize] + public async Task DeleteMessage(long roomId, Guid messageId) + { + if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); + + var message = await db.ChatMessages + .Include(m => m.Sender) + .FirstOrDefaultAsync(m => m.Id == messageId && m.ChatRoomId == roomId); + if (message == null) return NotFound(); + + if (message.Sender.AccountId != currentUser.Id) + return StatusCode(403, "You can only delete your own messages."); + + db.ChatMessages.Remove(message); + await db.SaveChangesAsync(); + + return Ok(); + } + public class SyncRequest { [Required] public long LastSyncTimestamp { get; set; } } - [HttpGet("{roomId:long}/sync")] + [HttpPost("{roomId:long}/sync")] public async Task> GetSyncData([FromBody] SyncRequest request, long roomId) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) diff --git a/DysonNetwork.Sphere/Chat/ChatService.cs b/DysonNetwork.Sphere/Chat/ChatService.cs index e8102ab..e5424aa 100644 --- a/DysonNetwork.Sphere/Chat/ChatService.cs +++ b/DysonNetwork.Sphere/Chat/ChatService.cs @@ -9,6 +9,9 @@ public class ChatService(AppDatabase db, IServiceScopeFactory scopeFactory) { public async Task SendMessageAsync(Message message, ChatMember sender, ChatRoom room) { + message.CreatedAt = SystemClock.Instance.GetCurrentInstant(); + message.UpdatedAt = message.CreatedAt; + // First complete the save operation db.ChatMessages.Add(message); await db.SaveChangesAsync(); @@ -23,7 +26,7 @@ public class ChatService(AppDatabase db, IServiceScopeFactory scopeFactory) public async Task DeliverMessageAsync(Message message, ChatMember sender, ChatRoom room) { - var scope = scopeFactory.CreateScope(); + using var scope = scopeFactory.CreateScope(); var scopedDb = scope.ServiceProvider.GetRequiredService(); var scopedWs = scope.ServiceProvider.GetRequiredService(); var scopedNty = scope.ServiceProvider.GetRequiredService(); @@ -32,7 +35,7 @@ public class ChatService(AppDatabase db, IServiceScopeFactory scopeFactory) var tasks = new List(); var members = await scopedDb.ChatMembers - .Where(m => m.ChatRoomId == message.ChatRoomId) + .Where(m => m.ChatRoomId == message.ChatRoomId && m.AccountId != sender.AccountId) .Where(m => m.Notify != ChatMemberNotify.None) .Where(m => m.Notify != ChatMemberNotify.Mentions || (message.MembersMentioned != null && message.MembersMentioned.Contains(m.Id))) @@ -103,14 +106,17 @@ public class ChatService(AppDatabase db, IServiceScopeFactory scopeFactory) var timestamp = Instant.FromUnixTimeMilliseconds(lastSyncTimestamp); var changes = await db.ChatMessages .IgnoreQueryFilters() + .Include(e => e.Sender) + .Include(e => e.Sender.Account) + .Include(e => e.Sender.Account.Profile) .Where(m => m.ChatRoomId == roomId) .Where(m => m.UpdatedAt > timestamp || m.DeletedAt > timestamp) .Select(m => new MessageChange { MessageId = m.Id, - Action = m.DeletedAt != null ? "delete" : (m.UpdatedAt == null ? "create" : "update"), + Action = m.DeletedAt != null ? "delete" : (m.UpdatedAt == m.CreatedAt ? "create" : "update"), Message = m.DeletedAt != null ? null : m, - Timestamp = m.DeletedAt != null ? m.DeletedAt.Value : m.UpdatedAt + Timestamp = m.DeletedAt ?? m.UpdatedAt }) .ToListAsync(); diff --git a/DysonNetwork.Sphere/Connection/WebSocketPacket.cs b/DysonNetwork.Sphere/Connection/WebSocketPacket.cs index d079e64..660b392 100644 --- a/DysonNetwork.Sphere/Connection/WebSocketPacket.cs +++ b/DysonNetwork.Sphere/Connection/WebSocketPacket.cs @@ -1,4 +1,6 @@ using System.Text.Json; +using NodaTime; +using NodaTime.Serialization.SystemTextJson; public class WebSocketPacketType { @@ -55,7 +57,7 @@ public class WebSocketPacket { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower, - }; + }.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);; var json = JsonSerializer.Serialize(this, jsonOpts); return System.Text.Encoding.UTF8.GetBytes(json); } diff --git a/DysonNetwork.Sphere/Connection/WebSocketService.cs b/DysonNetwork.Sphere/Connection/WebSocketService.cs index 9852b64..879905f 100644 --- a/DysonNetwork.Sphere/Connection/WebSocketService.cs +++ b/DysonNetwork.Sphere/Connection/WebSocketService.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Net.WebSockets; -using DysonNetwork.Sphere.Chat; namespace DysonNetwork.Sphere.Connection; diff --git a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj index f696cd3..f84dd2e 100644 --- a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj +++ b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj @@ -28,6 +28,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.Designer.cs b/DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.Designer.cs new file mode 100644 index 0000000..1d91f30 --- /dev/null +++ b/DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.Designer.cs @@ -0,0 +1,2359 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using DysonNetwork.Sphere; +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("20250503124624_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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.Sphere.Account.AccountAuthFactor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("Secret") + .HasColumnType("text") + .HasColumnName("secret"); + + 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.Sphere.Account.AccountContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("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.Sphere.Account.MagicSpell", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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.Sphere.Account.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_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>("Meta") + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("Subtitle") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)") + .HasColumnName("subtitle"); + + b.Property("Title") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("title"); + + b.Property("Topic") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("topic"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("ViewedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("viewed_at"); + + b.HasKey("Id") + .HasName("pk_notifications"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_notifications_account_id"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.NotificationPushSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("DeviceId") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("device_id"); + + b.Property("DeviceToken") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("device_token"); + + b.Property("LastUsedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_used_at"); + + b.Property("Provider") + .HasColumnType("integer") + .HasColumnName("provider"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_notification_push_subscriptions"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_notification_push_subscriptions_account_id"); + + b.HasIndex("DeviceId") + .IsUnique() + .HasDatabaseName("ix_notification_push_subscriptions_device_id"); + + b.HasIndex("DeviceToken") + .IsUnique() + .HasDatabaseName("ix_notification_push_subscriptions_device_token"); + + b.ToTable("notification_push_subscriptions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Profile", b => + { + b.Property("Id") + .HasColumnType("bigint") + .HasColumnName("id"); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("FirstName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("first_name"); + + b.Property("LastName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("last_name"); + + b.Property("MiddleName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("middle_name"); + + b.Property("PictureId") + .HasColumnType("character varying(128)") + .HasColumnName("picture_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_account_profiles"); + + b.HasIndex("BackgroundId") + .HasDatabaseName("ix_account_profiles_background_id"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_account_profiles_picture_id"); + + b.ToTable("account_profiles", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Relationship", b => + { + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("RelatedId") + .HasColumnType("bigint") + .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("integer") + .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.Sphere.Activity.Activity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("ResourceIdentifier") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("resource_identifier"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Visibility") + .HasColumnType("integer") + .HasColumnName("visibility"); + + b.HasKey("Id") + .HasName("pk_activities"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_activities_account_id"); + + b.ToTable("activities", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Auth.Challenge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("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.Sphere.Auth.Session", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_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.Sphere.Chat.ChatMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("ChatRoomId") + .HasColumnType("bigint") + .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("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("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.HasIndex("AccountId") + .HasDatabaseName("ix_chat_members_account_id"); + + b.ToTable("chat_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("IsPublic") + .HasColumnType("boolean") + .HasColumnName("is_public"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("PictureId") + .HasColumnType("character varying(128)") + .HasColumnName("picture_id"); + + b.Property("RealmId") + .HasColumnType("bigint") + .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("BackgroundId") + .HasDatabaseName("ix_chat_rooms_background_id"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_chat_rooms_picture_id"); + + 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("ChatRoomId") + .HasColumnType("bigint") + .HasColumnName("chat_room_id"); + + b.Property("Content") + .IsRequired() + .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("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_message_reaction"); + + b.HasIndex("MessageId") + .HasDatabaseName("ix_message_reaction_message_id"); + + b.HasIndex("SenderId") + .HasDatabaseName("ix_message_reaction_sender_id"); + + b.ToTable("message_reaction", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageStatus", b => + { + b.Property("MessageId") + .HasColumnType("uuid") + .HasColumnName("message_id"); + + b.Property("SenderId") + .HasColumnType("uuid") + .HasColumnName("sender_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("ReadAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("read_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("MessageId", "SenderId") + .HasName("pk_chat_statuses"); + + b.HasIndex("SenderId") + .HasDatabaseName("ix_chat_statuses_sender_id"); + + b.ToTable("chat_statuses", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.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.Sphere.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.Sphere.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.Sphere.Post.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .HasColumnType("jsonb") + .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("bigint") + .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("bigint") + .HasColumnName("publisher_id"); + + b.Property("RepliedPostId") + .HasColumnType("bigint") + .HasColumnName("replied_post_id"); + + b.Property("SearchVector") + .HasColumnType("tsvector") + .HasColumnName("search_vector"); + + b.Property("ThreadedPostId") + .HasColumnType("bigint") + .HasColumnName("threaded_post_id"); + + 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.HasIndex("ThreadedPostId") + .IsUnique() + .HasDatabaseName("ix_posts_threaded_post_id"); + + b.ToTable("posts", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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("bigint") + .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("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("bigint") + .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("AccountId") + .HasDatabaseName("ix_post_reactions_account_id"); + + 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("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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.Post.Publisher", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("PictureId") + .HasColumnType("character varying(128)") + .HasColumnName("picture_id"); + + b.Property("PublisherType") + .HasColumnType("integer") + .HasColumnName("publisher_type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_publishers"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_publishers_account_id"); + + b.HasIndex("BackgroundId") + .HasDatabaseName("ix_publishers_background_id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_publishers_name"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_publishers_picture_id"); + + b.ToTable("publishers", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PublisherMember", b => + { + b.Property("PublisherId") + .HasColumnType("bigint") + .HasColumnName("publisher_id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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.HasIndex("AccountId") + .HasDatabaseName("ix_publisher_members_account_id"); + + b.ToTable("publisher_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("PictureId") + .HasColumnType("character varying(128)") + .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("VerifiedAs") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("verified_as"); + + b.Property("VerifiedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("verified_at"); + + b.HasKey("Id") + .HasName("pk_realms"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_realms_account_id"); + + b.HasIndex("BackgroundId") + .HasDatabaseName("ix_realms_background_id"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_realms_picture_id"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("ix_realms_slug"); + + b.ToTable("realms", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmMember", b => + { + b.Property("RealmId") + .HasColumnType("bigint") + .HasColumnName("realm_id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("RealmId", "AccountId") + .HasName("pk_realm_members"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_realm_members_account_id"); + + b.ToTable("realm_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b => + { + b.Property("Id") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property>("FileMeta") + .HasColumnType("jsonb") + .HasColumnName("file_meta"); + + b.Property("HasCompression") + .HasColumnType("boolean") + .HasColumnName("has_compression"); + + b.Property("Hash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("hash"); + + b.Property("MessageId") + .HasColumnType("uuid") + .HasColumnName("message_id"); + + b.Property("MimeType") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("mime_type"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("PostId") + .HasColumnType("bigint") + .HasColumnName("post_id"); + + b.Property("Size") + .HasColumnType("bigint") + .HasColumnName("size"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UploadedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("uploaded_at"); + + b.Property("UploadedTo") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("uploaded_to"); + + b.Property("UsedCount") + .HasColumnType("integer") + .HasColumnName("used_count"); + + b.Property>("UserMeta") + .HasColumnType("jsonb") + .HasColumnName("user_meta"); + + b.HasKey("Id") + .HasName("pk_files"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_files_account_id"); + + b.HasIndex("MessageId") + .HasDatabaseName("ix_files_message_id"); + + b.HasIndex("PostId") + .HasDatabaseName("ix_files_post_id"); + + b.ToTable("files", (string)null); + }); + + modelBuilder.Entity("PostPostCategory", b => + { + b.Property("CategoriesId") + .HasColumnType("bigint") + .HasColumnName("categories_id"); + + b.Property("PostsId") + .HasColumnType("bigint") + .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("bigint") + .HasColumnName("collections_id"); + + b.Property("PostsId") + .HasColumnType("bigint") + .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("bigint") + .HasColumnName("posts_id"); + + b.Property("TagsId") + .HasColumnType("bigint") + .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.Account.AccountAuthFactor", b => + { + b.HasOne("DysonNetwork.Sphere.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.Sphere.Account.AccountContact", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("Contacts") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_contacts_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.MagicSpell", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .HasConstraintName("fk_magic_spells_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Notification", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_notifications_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.NotificationPushSubscription", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_notification_push_subscriptions_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Profile", b => + { + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_account_profiles_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithOne("Profile") + .HasForeignKey("DysonNetwork.Sphere.Account.Profile", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_profiles_accounts_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_account_profiles_files_picture_id"); + + b.Navigation("Account"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Relationship", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("OutgoingRelationships") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_relationships_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.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.Sphere.Activity.Activity", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_activities_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Auth.Challenge", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("Challenges") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_auth_challenges_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Auth.Session", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("Sessions") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_auth_sessions_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Auth.Challenge", "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.Sphere.Chat.ChatMember", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_members_accounts_account_id"); + + 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("Account"); + + b.Navigation("ChatRoom"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_chat_rooms_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_chat_rooms_files_picture_id"); + + b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") + .WithMany("ChatRooms") + .HasForeignKey("RealmId") + .HasConstraintName("fk_chat_rooms_realms_realm_id"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + + 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_message_reaction_chat_messages_message_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_message_reaction_chat_members_sender_id"); + + b.Navigation("Message"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageStatus", b => + { + b.HasOne("DysonNetwork.Sphere.Chat.Message", "Message") + .WithMany("Statuses") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_statuses_chat_messages_message_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_statuses_chat_members_sender_id"); + + b.Navigation("Message"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroupMember", b => + { + b.HasOne("DysonNetwork.Sphere.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.Sphere.Permission.PermissionNode", b => + { + b.HasOne("DysonNetwork.Sphere.Permission.PermissionGroup", "Group") + .WithMany("Nodes") + .HasForeignKey("GroupId") + .HasConstraintName("fk_permission_nodes_permission_groups_group_id"); + + b.Navigation("Group"); + }); + + 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.Post.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.HasOne("DysonNetwork.Sphere.Post.Post", "ThreadedPost") + .WithOne() + .HasForeignKey("DysonNetwork.Sphere.Post.Post", "ThreadedPostId") + .HasConstraintName("fk_posts_posts_threaded_post_id"); + + b.Navigation("ForwardedPost"); + + b.Navigation("Publisher"); + + b.Navigation("RepliedPost"); + + b.Navigation("ThreadedPost"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b => + { + b.HasOne("DysonNetwork.Sphere.Post.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.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_reactions_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Post", "Post") + .WithMany("Reactions") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_reactions_posts_post_id"); + + b.Navigation("Account"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .HasConstraintName("fk_publishers_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_publishers_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_publishers_files_picture_id"); + + b.Navigation("Account"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PublisherMember", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publisher_members_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Publisher", "Publisher") + .WithMany("Members") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publisher_members_publishers_publisher_id"); + + b.Navigation("Account"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_realms_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_realms_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_realms_files_picture_id"); + + b.Navigation("Account"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmMember", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_realm_members_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") + .WithMany("Members") + .HasForeignKey("RealmId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_realm_members_realms_realm_id"); + + b.Navigation("Account"); + + b.Navigation("Realm"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_files_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.Message", null) + .WithMany("Attachments") + .HasForeignKey("MessageId") + .HasConstraintName("fk_files_chat_messages_message_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Post", null) + .WithMany("Attachments") + .HasForeignKey("PostId") + .HasConstraintName("fk_files_posts_post_id"); + + b.Navigation("Account"); + }); + + 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.Account.Account", b => + { + b.Navigation("AuthFactors"); + + b.Navigation("Challenges"); + + b.Navigation("Contacts"); + + b.Navigation("IncomingRelationships"); + + b.Navigation("OutgoingRelationships"); + + b.Navigation("Profile") + .IsRequired(); + + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.Navigation("Members"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b => + { + b.Navigation("Attachments"); + + b.Navigation("Reactions"); + + b.Navigation("Statuses"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b => + { + b.Navigation("Members"); + + b.Navigation("Nodes"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Post", b => + { + b.Navigation("Attachments"); + + b.Navigation("Reactions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b => + { + b.Navigation("Collections"); + + b.Navigation("Members"); + + b.Navigation("Posts"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.Navigation("ChatRooms"); + + b.Navigation("Members"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.cs b/DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.cs new file mode 100644 index 0000000..8304822 --- /dev/null +++ b/DysonNetwork.Sphere/Migrations/20250503124624_InitialMigration.cs @@ -0,0 +1,1389 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace DysonNetwork.Sphere.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "accounts", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + nick = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + language = table.Column(type: "character varying(32)", maxLength: 32, nullable: false), + activated_at = table.Column(type: "timestamp with time zone", nullable: true), + is_superuser = table.Column(type: "boolean", 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_accounts", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "permission_groups", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + key = table.Column(type: "character varying(1024)", maxLength: 1024, 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_permission_groups", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "post_categories", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + slug = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + name = table.Column(type: "character varying(256)", maxLength: 256, 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) + }, + constraints: table => + { + table.PrimaryKey("pk_post_categories", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "post_tags", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + slug = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + name = table.Column(type: "character varying(256)", maxLength: 256, 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) + }, + constraints: table => + { + table.PrimaryKey("pk_post_tags", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "account_auth_factors", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + type = table.Column(type: "integer", nullable: false), + secret = table.Column(type: "text", nullable: true), + account_id = table.Column(type: "bigint", 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_account_auth_factors", x => x.id); + table.ForeignKey( + name: "fk_account_auth_factors_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "account_contacts", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + type = table.Column(type: "integer", nullable: false), + verified_at = table.Column(type: "timestamp with time zone", nullable: true), + content = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + account_id = table.Column(type: "bigint", 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_account_contacts", x => x.id); + table.ForeignKey( + name: "fk_account_contacts_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "account_relationships", + columns: table => new + { + account_id = table.Column(type: "bigint", nullable: false), + related_id = table.Column(type: "bigint", nullable: false), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + status = table.Column(type: "integer", 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_account_relationships", x => new { x.account_id, x.related_id }); + table.ForeignKey( + name: "fk_account_relationships_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_account_relationships_accounts_related_id", + column: x => x.related_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "activities", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + type = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + resource_identifier = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: false), + visibility = table.Column(type: "integer", nullable: false), + account_id = table.Column(type: "bigint", 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_activities", x => x.id); + table.ForeignKey( + name: "fk_activities_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "auth_challenges", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + step_remain = table.Column(type: "integer", nullable: false), + step_total = table.Column(type: "integer", nullable: false), + failed_attempts = table.Column(type: "integer", nullable: false), + platform = table.Column(type: "integer", nullable: false), + type = table.Column(type: "integer", nullable: false), + blacklist_factors = table.Column>(type: "jsonb", nullable: false), + audiences = table.Column>(type: "jsonb", nullable: false), + scopes = table.Column>(type: "jsonb", nullable: false), + ip_address = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + user_agent = table.Column(type: "character varying(512)", maxLength: 512, nullable: true), + device_id = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + nonce = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + account_id = table.Column(type: "bigint", 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_auth_challenges", x => x.id); + table.ForeignKey( + name: "fk_auth_challenges_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "magic_spells", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + spell = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + type = table.Column(type: "integer", nullable: false), + expires_at = table.Column(type: "timestamp with time zone", nullable: true), + affected_at = table.Column(type: "timestamp with time zone", nullable: true), + meta = table.Column>(type: "jsonb", nullable: false), + account_id = table.Column(type: "bigint", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_magic_spells", x => x.id); + table.ForeignKey( + name: "fk_magic_spells_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id"); + }); + + migrationBuilder.CreateTable( + name: "notification_push_subscriptions", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + device_id = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: false), + device_token = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: false), + provider = table.Column(type: "integer", nullable: false), + last_used_at = table.Column(type: "timestamp with time zone", nullable: true), + account_id = table.Column(type: "bigint", 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_notification_push_subscriptions", x => x.id); + table.ForeignKey( + name: "fk_notification_push_subscriptions_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "notifications", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + topic = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + title = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + subtitle = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + content = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + meta = table.Column>(type: "jsonb", nullable: true), + priority = table.Column(type: "integer", nullable: false), + viewed_at = table.Column(type: "timestamp with time zone", nullable: true), + account_id = table.Column(type: "bigint", 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_notifications", x => x.id); + table.ForeignKey( + name: "fk_notifications_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "permission_group_members", + columns: table => new + { + group_id = table.Column(type: "uuid", nullable: false), + actor = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + affected_at = table.Column(type: "timestamp with time zone", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_permission_group_members", x => new { x.group_id, x.actor }); + table.ForeignKey( + name: "fk_permission_group_members_permission_groups_group_id", + column: x => x.group_id, + principalTable: "permission_groups", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "permission_nodes", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + actor = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + area = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + key = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + value = table.Column(type: "jsonb", nullable: false), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + affected_at = table.Column(type: "timestamp with time zone", nullable: true), + group_id = table.Column(type: "uuid", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_permission_nodes", x => x.id); + table.ForeignKey( + name: "fk_permission_nodes_permission_groups_group_id", + column: x => x.group_id, + principalTable: "permission_groups", + principalColumn: "id"); + }); + + migrationBuilder.CreateTable( + name: "auth_sessions", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + label = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + last_granted_at = table.Column(type: "timestamp with time zone", nullable: true), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + account_id = table.Column(type: "bigint", nullable: false), + challenge_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_auth_sessions", x => x.id); + table.ForeignKey( + name: "fk_auth_sessions_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_auth_sessions_auth_challenges_challenge_id", + column: x => x.challenge_id, + principalTable: "auth_challenges", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "account_profiles", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false), + first_name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + middle_name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + last_name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + bio = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + picture_id = table.Column(type: "character varying(128)", nullable: true), + background_id = table.Column(type: "character varying(128)", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_account_profiles", x => x.id); + table.ForeignKey( + name: "fk_account_profiles_accounts_id", + column: x => x.id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "chat_members", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + chat_room_id = table.Column(type: "bigint", nullable: false), + account_id = table.Column(type: "bigint", nullable: false), + nick = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + role = table.Column(type: "integer", nullable: false), + notify = table.Column(type: "integer", nullable: false), + joined_at = table.Column(type: "timestamp with time zone", nullable: true), + is_bot = table.Column(type: "boolean", 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_chat_members", x => x.id); + table.UniqueConstraint("ak_chat_members_chat_room_id_account_id", x => new { x.chat_room_id, x.account_id }); + table.ForeignKey( + name: "fk_chat_members_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "chat_messages", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + content = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: false), + meta = table.Column>(type: "jsonb", nullable: true), + members_mentioned = table.Column>(type: "jsonb", nullable: true), + nonce = table.Column(type: "character varying(36)", maxLength: 36, nullable: false), + edited_at = table.Column(type: "timestamp with time zone", nullable: true), + replied_message_id = table.Column(type: "uuid", nullable: true), + forwarded_message_id = table.Column(type: "uuid", nullable: true), + sender_id = table.Column(type: "uuid", nullable: false), + chat_room_id = table.Column(type: "bigint", 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_chat_messages", x => x.id); + table.ForeignKey( + name: "fk_chat_messages_chat_members_sender_id", + column: x => x.sender_id, + principalTable: "chat_members", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_chat_messages_chat_messages_forwarded_message_id", + column: x => x.forwarded_message_id, + principalTable: "chat_messages", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_chat_messages_chat_messages_replied_message_id", + column: x => x.replied_message_id, + principalTable: "chat_messages", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "chat_statuses", + columns: table => new + { + message_id = table.Column(type: "uuid", nullable: false), + sender_id = table.Column(type: "uuid", nullable: false), + read_at = table.Column(type: "timestamp with time zone", 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_chat_statuses", x => new { x.message_id, x.sender_id }); + table.ForeignKey( + name: "fk_chat_statuses_chat_members_sender_id", + column: x => x.sender_id, + principalTable: "chat_members", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_chat_statuses_chat_messages_message_id", + column: x => x.message_id, + principalTable: "chat_messages", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "message_reaction", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + message_id = table.Column(type: "uuid", nullable: false), + sender_id = table.Column(type: "uuid", nullable: false), + symbol = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + attitude = table.Column(type: "integer", 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_message_reaction", x => x.id); + table.ForeignKey( + name: "fk_message_reaction_chat_members_sender_id", + column: x => x.sender_id, + principalTable: "chat_members", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_message_reaction_chat_messages_message_id", + column: x => x.message_id, + principalTable: "chat_messages", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "chat_rooms", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: false), + type = table.Column(type: "integer", nullable: false), + is_public = table.Column(type: "boolean", nullable: false), + picture_id = table.Column(type: "character varying(128)", nullable: true), + background_id = table.Column(type: "character varying(128)", nullable: true), + realm_id = table.Column(type: "bigint", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_chat_rooms", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "files", + columns: table => new + { + id = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + name = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: false), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + file_meta = table.Column>(type: "jsonb", nullable: true), + user_meta = table.Column>(type: "jsonb", nullable: true), + mime_type = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + hash = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + size = table.Column(type: "bigint", nullable: false), + uploaded_at = table.Column(type: "timestamp with time zone", nullable: true), + expired_at = table.Column(type: "timestamp with time zone", nullable: true), + uploaded_to = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + has_compression = table.Column(type: "boolean", nullable: false), + used_count = table.Column(type: "integer", nullable: false), + account_id = table.Column(type: "bigint", nullable: false), + message_id = table.Column(type: "uuid", nullable: true), + post_id = table.Column(type: "bigint", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_files", x => x.id); + table.ForeignKey( + name: "fk_files_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_files_chat_messages_message_id", + column: x => x.message_id, + principalTable: "chat_messages", + principalColumn: "id"); + }); + + migrationBuilder.CreateTable( + name: "publishers", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + publisher_type = table.Column(type: "integer", nullable: false), + name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + nick = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + bio = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + picture_id = table.Column(type: "character varying(128)", nullable: true), + background_id = table.Column(type: "character varying(128)", nullable: true), + account_id = table.Column(type: "bigint", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_publishers", x => x.id); + table.ForeignKey( + name: "fk_publishers_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id"); + table.ForeignKey( + name: "fk_publishers_files_background_id", + column: x => x.background_id, + principalTable: "files", + principalColumn: "id"); + table.ForeignKey( + name: "fk_publishers_files_picture_id", + column: x => x.picture_id, + principalTable: "files", + principalColumn: "id"); + }); + + migrationBuilder.CreateTable( + name: "realms", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + 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: false), + verified_as = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + verified_at = table.Column(type: "timestamp with time zone", nullable: true), + is_community = table.Column(type: "boolean", nullable: false), + is_public = table.Column(type: "boolean", nullable: false), + picture_id = table.Column(type: "character varying(128)", nullable: true), + background_id = table.Column(type: "character varying(128)", nullable: true), + account_id = table.Column(type: "bigint", 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_realms", x => x.id); + table.ForeignKey( + name: "fk_realms_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_realms_files_background_id", + column: x => x.background_id, + principalTable: "files", + principalColumn: "id"); + table.ForeignKey( + name: "fk_realms_files_picture_id", + column: x => x.picture_id, + principalTable: "files", + principalColumn: "id"); + }); + + migrationBuilder.CreateTable( + name: "post_collections", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + slug = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + publisher_id = table.Column(type: "bigint", 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_post_collections", x => x.id); + table.ForeignKey( + name: "fk_post_collections_publishers_publisher_id", + column: x => x.publisher_id, + principalTable: "publishers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "posts", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + title = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + description = table.Column(type: "character varying(4096)", maxLength: 4096, nullable: true), + language = table.Column(type: "character varying(128)", maxLength: 128, nullable: true), + edited_at = table.Column(type: "timestamp with time zone", nullable: true), + published_at = table.Column(type: "timestamp with time zone", nullable: true), + visibility = table.Column(type: "integer", nullable: false), + content = table.Column(type: "jsonb", nullable: true), + type = table.Column(type: "integer", nullable: false), + meta = table.Column>(type: "jsonb", nullable: true), + views_unique = table.Column(type: "integer", nullable: false), + views_total = table.Column(type: "integer", nullable: false), + upvotes = table.Column(type: "integer", nullable: false), + downvotes = table.Column(type: "integer", nullable: false), + threaded_post_id = table.Column(type: "bigint", nullable: true), + replied_post_id = table.Column(type: "bigint", nullable: true), + forwarded_post_id = table.Column(type: "bigint", nullable: true), + search_vector = table.Column(type: "tsvector", nullable: true), + publisher_id = table.Column(type: "bigint", 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_posts", x => x.id); + table.ForeignKey( + name: "fk_posts_posts_forwarded_post_id", + column: x => x.forwarded_post_id, + principalTable: "posts", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_posts_posts_replied_post_id", + column: x => x.replied_post_id, + principalTable: "posts", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_posts_posts_threaded_post_id", + column: x => x.threaded_post_id, + principalTable: "posts", + principalColumn: "id"); + table.ForeignKey( + name: "fk_posts_publishers_publisher_id", + column: x => x.publisher_id, + principalTable: "publishers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "publisher_members", + columns: table => new + { + publisher_id = table.Column(type: "bigint", nullable: false), + account_id = table.Column(type: "bigint", nullable: false), + role = table.Column(type: "integer", nullable: false), + joined_at = table.Column(type: "timestamp with time zone", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_publisher_members", x => new { x.publisher_id, x.account_id }); + table.ForeignKey( + name: "fk_publisher_members_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_publisher_members_publishers_publisher_id", + column: x => x.publisher_id, + principalTable: "publishers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "realm_members", + columns: table => new + { + realm_id = table.Column(type: "bigint", nullable: false), + account_id = table.Column(type: "bigint", nullable: false), + role = table.Column(type: "integer", nullable: false), + joined_at = table.Column(type: "timestamp with time zone", 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) + }, + constraints: table => + { + table.PrimaryKey("pk_realm_members", x => new { x.realm_id, x.account_id }); + table.ForeignKey( + name: "fk_realm_members_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_realm_members_realms_realm_id", + column: x => x.realm_id, + principalTable: "realms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "post_category_links", + columns: table => new + { + categories_id = table.Column(type: "bigint", nullable: false), + posts_id = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_post_category_links", x => new { x.categories_id, x.posts_id }); + table.ForeignKey( + name: "fk_post_category_links_post_categories_categories_id", + column: x => x.categories_id, + principalTable: "post_categories", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_post_category_links_posts_posts_id", + column: x => x.posts_id, + principalTable: "posts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "post_collection_links", + columns: table => new + { + collections_id = table.Column(type: "bigint", nullable: false), + posts_id = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_post_collection_links", x => new { x.collections_id, x.posts_id }); + table.ForeignKey( + name: "fk_post_collection_links_post_collections_collections_id", + column: x => x.collections_id, + principalTable: "post_collections", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_post_collection_links_posts_posts_id", + column: x => x.posts_id, + principalTable: "posts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "post_reactions", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + symbol = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + attitude = table.Column(type: "integer", nullable: false), + post_id = table.Column(type: "bigint", nullable: false), + account_id = table.Column(type: "bigint", 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_post_reactions", x => x.id); + table.ForeignKey( + name: "fk_post_reactions_accounts_account_id", + column: x => x.account_id, + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_post_reactions_posts_post_id", + column: x => x.post_id, + principalTable: "posts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "post_tag_links", + columns: table => new + { + posts_id = table.Column(type: "bigint", nullable: false), + tags_id = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_post_tag_links", x => new { x.posts_id, x.tags_id }); + table.ForeignKey( + name: "fk_post_tag_links_post_tags_tags_id", + column: x => x.tags_id, + principalTable: "post_tags", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_post_tag_links_posts_posts_id", + column: x => x.posts_id, + principalTable: "posts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_account_auth_factors_account_id", + table: "account_auth_factors", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_account_contacts_account_id", + table: "account_contacts", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_account_profiles_background_id", + table: "account_profiles", + column: "background_id"); + + migrationBuilder.CreateIndex( + name: "ix_account_profiles_picture_id", + table: "account_profiles", + column: "picture_id"); + + migrationBuilder.CreateIndex( + name: "ix_account_relationships_related_id", + table: "account_relationships", + column: "related_id"); + + migrationBuilder.CreateIndex( + name: "ix_accounts_name", + table: "accounts", + column: "name", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_activities_account_id", + table: "activities", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_auth_challenges_account_id", + table: "auth_challenges", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_auth_sessions_account_id", + table: "auth_sessions", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_auth_sessions_challenge_id", + table: "auth_sessions", + column: "challenge_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_members_account_id", + table: "chat_members", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_messages_chat_room_id", + table: "chat_messages", + column: "chat_room_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_messages_forwarded_message_id", + table: "chat_messages", + column: "forwarded_message_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_messages_replied_message_id", + table: "chat_messages", + column: "replied_message_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_messages_sender_id", + table: "chat_messages", + column: "sender_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_rooms_background_id", + table: "chat_rooms", + column: "background_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_rooms_picture_id", + table: "chat_rooms", + column: "picture_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_rooms_realm_id", + table: "chat_rooms", + column: "realm_id"); + + migrationBuilder.CreateIndex( + name: "ix_chat_statuses_sender_id", + table: "chat_statuses", + column: "sender_id"); + + migrationBuilder.CreateIndex( + name: "ix_files_account_id", + table: "files", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_files_message_id", + table: "files", + column: "message_id"); + + migrationBuilder.CreateIndex( + name: "ix_files_post_id", + table: "files", + column: "post_id"); + + migrationBuilder.CreateIndex( + name: "ix_magic_spells_account_id", + table: "magic_spells", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_magic_spells_spell", + table: "magic_spells", + column: "spell", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_message_reaction_message_id", + table: "message_reaction", + column: "message_id"); + + migrationBuilder.CreateIndex( + name: "ix_message_reaction_sender_id", + table: "message_reaction", + column: "sender_id"); + + migrationBuilder.CreateIndex( + name: "ix_notification_push_subscriptions_account_id", + table: "notification_push_subscriptions", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_notification_push_subscriptions_device_id", + table: "notification_push_subscriptions", + column: "device_id", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_notification_push_subscriptions_device_token", + table: "notification_push_subscriptions", + column: "device_token", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_notifications_account_id", + table: "notifications", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_permission_nodes_group_id", + table: "permission_nodes", + column: "group_id"); + + migrationBuilder.CreateIndex( + name: "ix_permission_nodes_key_area_actor", + table: "permission_nodes", + columns: new[] { "key", "area", "actor" }); + + migrationBuilder.CreateIndex( + name: "ix_post_category_links_posts_id", + table: "post_category_links", + column: "posts_id"); + + migrationBuilder.CreateIndex( + name: "ix_post_collection_links_posts_id", + table: "post_collection_links", + column: "posts_id"); + + migrationBuilder.CreateIndex( + name: "ix_post_collections_publisher_id", + table: "post_collections", + column: "publisher_id"); + + migrationBuilder.CreateIndex( + name: "ix_post_reactions_account_id", + table: "post_reactions", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_post_reactions_post_id", + table: "post_reactions", + column: "post_id"); + + migrationBuilder.CreateIndex( + name: "ix_post_tag_links_tags_id", + table: "post_tag_links", + column: "tags_id"); + + migrationBuilder.CreateIndex( + name: "ix_posts_forwarded_post_id", + table: "posts", + column: "forwarded_post_id"); + + migrationBuilder.CreateIndex( + name: "ix_posts_publisher_id", + table: "posts", + column: "publisher_id"); + + migrationBuilder.CreateIndex( + name: "ix_posts_replied_post_id", + table: "posts", + column: "replied_post_id"); + + migrationBuilder.CreateIndex( + name: "ix_posts_search_vector", + table: "posts", + column: "search_vector") + .Annotation("Npgsql:IndexMethod", "GIN"); + + migrationBuilder.CreateIndex( + name: "ix_posts_threaded_post_id", + table: "posts", + column: "threaded_post_id", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_publisher_members_account_id", + table: "publisher_members", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_publishers_account_id", + table: "publishers", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_publishers_background_id", + table: "publishers", + column: "background_id"); + + migrationBuilder.CreateIndex( + name: "ix_publishers_name", + table: "publishers", + column: "name", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_publishers_picture_id", + table: "publishers", + column: "picture_id"); + + migrationBuilder.CreateIndex( + name: "ix_realm_members_account_id", + table: "realm_members", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_realms_account_id", + table: "realms", + column: "account_id"); + + migrationBuilder.CreateIndex( + name: "ix_realms_background_id", + table: "realms", + column: "background_id"); + + migrationBuilder.CreateIndex( + name: "ix_realms_picture_id", + table: "realms", + column: "picture_id"); + + migrationBuilder.CreateIndex( + name: "ix_realms_slug", + table: "realms", + column: "slug", + unique: true); + + migrationBuilder.AddForeignKey( + name: "fk_account_profiles_files_background_id", + table: "account_profiles", + column: "background_id", + principalTable: "files", + principalColumn: "id"); + + migrationBuilder.AddForeignKey( + name: "fk_account_profiles_files_picture_id", + table: "account_profiles", + column: "picture_id", + principalTable: "files", + principalColumn: "id"); + + migrationBuilder.AddForeignKey( + name: "fk_chat_members_chat_rooms_chat_room_id", + table: "chat_members", + column: "chat_room_id", + principalTable: "chat_rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "fk_chat_messages_chat_rooms_chat_room_id", + table: "chat_messages", + column: "chat_room_id", + principalTable: "chat_rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "fk_chat_rooms_files_background_id", + table: "chat_rooms", + column: "background_id", + principalTable: "files", + principalColumn: "id"); + + migrationBuilder.AddForeignKey( + name: "fk_chat_rooms_files_picture_id", + table: "chat_rooms", + column: "picture_id", + principalTable: "files", + principalColumn: "id"); + + migrationBuilder.AddForeignKey( + name: "fk_chat_rooms_realms_realm_id", + table: "chat_rooms", + column: "realm_id", + principalTable: "realms", + principalColumn: "id"); + + migrationBuilder.AddForeignKey( + name: "fk_files_posts_post_id", + table: "files", + column: "post_id", + principalTable: "posts", + principalColumn: "id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "fk_chat_members_accounts_account_id", + table: "chat_members"); + + migrationBuilder.DropForeignKey( + name: "fk_files_accounts_account_id", + table: "files"); + + migrationBuilder.DropForeignKey( + name: "fk_publishers_accounts_account_id", + table: "publishers"); + + migrationBuilder.DropForeignKey( + name: "fk_realms_accounts_account_id", + table: "realms"); + + migrationBuilder.DropForeignKey( + name: "fk_chat_rooms_files_background_id", + table: "chat_rooms"); + + migrationBuilder.DropForeignKey( + name: "fk_chat_rooms_files_picture_id", + table: "chat_rooms"); + + migrationBuilder.DropForeignKey( + name: "fk_publishers_files_background_id", + table: "publishers"); + + migrationBuilder.DropForeignKey( + name: "fk_publishers_files_picture_id", + table: "publishers"); + + migrationBuilder.DropForeignKey( + name: "fk_realms_files_background_id", + table: "realms"); + + migrationBuilder.DropForeignKey( + name: "fk_realms_files_picture_id", + table: "realms"); + + migrationBuilder.DropTable( + name: "account_auth_factors"); + + migrationBuilder.DropTable( + name: "account_contacts"); + + migrationBuilder.DropTable( + name: "account_profiles"); + + migrationBuilder.DropTable( + name: "account_relationships"); + + migrationBuilder.DropTable( + name: "activities"); + + migrationBuilder.DropTable( + name: "auth_sessions"); + + migrationBuilder.DropTable( + name: "chat_statuses"); + + migrationBuilder.DropTable( + name: "magic_spells"); + + migrationBuilder.DropTable( + name: "message_reaction"); + + migrationBuilder.DropTable( + name: "notification_push_subscriptions"); + + migrationBuilder.DropTable( + name: "notifications"); + + migrationBuilder.DropTable( + name: "permission_group_members"); + + migrationBuilder.DropTable( + name: "permission_nodes"); + + migrationBuilder.DropTable( + name: "post_category_links"); + + migrationBuilder.DropTable( + name: "post_collection_links"); + + migrationBuilder.DropTable( + name: "post_reactions"); + + migrationBuilder.DropTable( + name: "post_tag_links"); + + migrationBuilder.DropTable( + name: "publisher_members"); + + migrationBuilder.DropTable( + name: "realm_members"); + + migrationBuilder.DropTable( + name: "auth_challenges"); + + migrationBuilder.DropTable( + name: "permission_groups"); + + migrationBuilder.DropTable( + name: "post_categories"); + + migrationBuilder.DropTable( + name: "post_collections"); + + migrationBuilder.DropTable( + name: "post_tags"); + + migrationBuilder.DropTable( + name: "accounts"); + + migrationBuilder.DropTable( + name: "files"); + + migrationBuilder.DropTable( + name: "chat_messages"); + + migrationBuilder.DropTable( + name: "posts"); + + migrationBuilder.DropTable( + name: "chat_members"); + + migrationBuilder.DropTable( + name: "publishers"); + + migrationBuilder.DropTable( + name: "chat_rooms"); + + migrationBuilder.DropTable( + name: "realms"); + } + } +} diff --git a/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs b/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs new file mode 100644 index 0000000..ce445a2 --- /dev/null +++ b/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs @@ -0,0 +1,2356 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using DysonNetwork.Sphere; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace DysonNetwork.Sphere.Migrations +{ + [DbContext(typeof(AppDatabase))] + partial class AppDatabaseModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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.Sphere.Account.AccountAuthFactor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("Secret") + .HasColumnType("text") + .HasColumnName("secret"); + + 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.Sphere.Account.AccountContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("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.Sphere.Account.MagicSpell", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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.Sphere.Account.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_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>("Meta") + .HasColumnType("jsonb") + .HasColumnName("meta"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("Subtitle") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)") + .HasColumnName("subtitle"); + + b.Property("Title") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("title"); + + b.Property("Topic") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("topic"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("ViewedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("viewed_at"); + + b.HasKey("Id") + .HasName("pk_notifications"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_notifications_account_id"); + + b.ToTable("notifications", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.NotificationPushSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("DeviceId") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("device_id"); + + b.Property("DeviceToken") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("device_token"); + + b.Property("LastUsedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_used_at"); + + b.Property("Provider") + .HasColumnType("integer") + .HasColumnName("provider"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_notification_push_subscriptions"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_notification_push_subscriptions_account_id"); + + b.HasIndex("DeviceId") + .IsUnique() + .HasDatabaseName("ix_notification_push_subscriptions_device_id"); + + b.HasIndex("DeviceToken") + .IsUnique() + .HasDatabaseName("ix_notification_push_subscriptions_device_token"); + + b.ToTable("notification_push_subscriptions", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Profile", b => + { + b.Property("Id") + .HasColumnType("bigint") + .HasColumnName("id"); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("FirstName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("first_name"); + + b.Property("LastName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("last_name"); + + b.Property("MiddleName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("middle_name"); + + b.Property("PictureId") + .HasColumnType("character varying(128)") + .HasColumnName("picture_id"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_account_profiles"); + + b.HasIndex("BackgroundId") + .HasDatabaseName("ix_account_profiles_background_id"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_account_profiles_picture_id"); + + b.ToTable("account_profiles", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Relationship", b => + { + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("RelatedId") + .HasColumnType("bigint") + .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("integer") + .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.Sphere.Activity.Activity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("ResourceIdentifier") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("resource_identifier"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Visibility") + .HasColumnType("integer") + .HasColumnName("visibility"); + + b.HasKey("Id") + .HasName("pk_activities"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_activities_account_id"); + + b.ToTable("activities", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Auth.Challenge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("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.Sphere.Auth.Session", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_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.Sphere.Chat.ChatMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("ChatRoomId") + .HasColumnType("bigint") + .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("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("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.HasIndex("AccountId") + .HasDatabaseName("ix_chat_members_account_id"); + + b.ToTable("chat_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("IsPublic") + .HasColumnType("boolean") + .HasColumnName("is_public"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("PictureId") + .HasColumnType("character varying(128)") + .HasColumnName("picture_id"); + + b.Property("RealmId") + .HasColumnType("bigint") + .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("BackgroundId") + .HasDatabaseName("ix_chat_rooms_background_id"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_chat_rooms_picture_id"); + + 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("ChatRoomId") + .HasColumnType("bigint") + .HasColumnName("chat_room_id"); + + b.Property("Content") + .IsRequired() + .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("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_message_reaction"); + + b.HasIndex("MessageId") + .HasDatabaseName("ix_message_reaction_message_id"); + + b.HasIndex("SenderId") + .HasDatabaseName("ix_message_reaction_sender_id"); + + b.ToTable("message_reaction", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageStatus", b => + { + b.Property("MessageId") + .HasColumnType("uuid") + .HasColumnName("message_id"); + + b.Property("SenderId") + .HasColumnType("uuid") + .HasColumnName("sender_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("ReadAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("read_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("MessageId", "SenderId") + .HasName("pk_chat_statuses"); + + b.HasIndex("SenderId") + .HasDatabaseName("ix_chat_statuses_sender_id"); + + b.ToTable("chat_statuses", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.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.Sphere.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.Sphere.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.Sphere.Post.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .HasColumnType("jsonb") + .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("bigint") + .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("bigint") + .HasColumnName("publisher_id"); + + b.Property("RepliedPostId") + .HasColumnType("bigint") + .HasColumnName("replied_post_id"); + + b.Property("SearchVector") + .HasColumnType("tsvector") + .HasColumnName("search_vector"); + + b.Property("ThreadedPostId") + .HasColumnType("bigint") + .HasColumnName("threaded_post_id"); + + 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.HasIndex("ThreadedPostId") + .IsUnique() + .HasDatabaseName("ix_posts_threaded_post_id"); + + b.ToTable("posts", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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("bigint") + .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("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("bigint") + .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("AccountId") + .HasDatabaseName("ix_post_reactions_account_id"); + + 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("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("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.Post.Publisher", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("PictureId") + .HasColumnType("character varying(128)") + .HasColumnName("picture_id"); + + b.Property("PublisherType") + .HasColumnType("integer") + .HasColumnName("publisher_type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id") + .HasName("pk_publishers"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_publishers_account_id"); + + b.HasIndex("BackgroundId") + .HasDatabaseName("ix_publishers_background_id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_publishers_name"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_publishers_picture_id"); + + b.ToTable("publishers", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PublisherMember", b => + { + b.Property("PublisherId") + .HasColumnType("bigint") + .HasColumnName("publisher_id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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.HasIndex("AccountId") + .HasDatabaseName("ix_publisher_members_account_id"); + + b.ToTable("publisher_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("BackgroundId") + .HasColumnType("character varying(128)") + .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("PictureId") + .HasColumnType("character varying(128)") + .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("VerifiedAs") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("verified_as"); + + b.Property("VerifiedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("verified_at"); + + b.HasKey("Id") + .HasName("pk_realms"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_realms_account_id"); + + b.HasIndex("BackgroundId") + .HasDatabaseName("ix_realms_background_id"); + + b.HasIndex("PictureId") + .HasDatabaseName("ix_realms_picture_id"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("ix_realms_slug"); + + b.ToTable("realms", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmMember", b => + { + b.Property("RealmId") + .HasColumnType("bigint") + .HasColumnName("realm_id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("RealmId", "AccountId") + .HasName("pk_realm_members"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_realm_members_account_id"); + + b.ToTable("realm_members", (string)null); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b => + { + b.Property("Id") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("bigint") + .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("Description") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("description"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expired_at"); + + b.Property>("FileMeta") + .HasColumnType("jsonb") + .HasColumnName("file_meta"); + + b.Property("HasCompression") + .HasColumnType("boolean") + .HasColumnName("has_compression"); + + b.Property("Hash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("hash"); + + b.Property("MessageId") + .HasColumnType("uuid") + .HasColumnName("message_id"); + + b.Property("MimeType") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("mime_type"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("name"); + + b.Property("PostId") + .HasColumnType("bigint") + .HasColumnName("post_id"); + + b.Property("Size") + .HasColumnType("bigint") + .HasColumnName("size"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("UploadedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("uploaded_at"); + + b.Property("UploadedTo") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("uploaded_to"); + + b.Property("UsedCount") + .HasColumnType("integer") + .HasColumnName("used_count"); + + b.Property>("UserMeta") + .HasColumnType("jsonb") + .HasColumnName("user_meta"); + + b.HasKey("Id") + .HasName("pk_files"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_files_account_id"); + + b.HasIndex("MessageId") + .HasDatabaseName("ix_files_message_id"); + + b.HasIndex("PostId") + .HasDatabaseName("ix_files_post_id"); + + b.ToTable("files", (string)null); + }); + + modelBuilder.Entity("PostPostCategory", b => + { + b.Property("CategoriesId") + .HasColumnType("bigint") + .HasColumnName("categories_id"); + + b.Property("PostsId") + .HasColumnType("bigint") + .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("bigint") + .HasColumnName("collections_id"); + + b.Property("PostsId") + .HasColumnType("bigint") + .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("bigint") + .HasColumnName("posts_id"); + + b.Property("TagsId") + .HasColumnType("bigint") + .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.Account.AccountAuthFactor", b => + { + b.HasOne("DysonNetwork.Sphere.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.Sphere.Account.AccountContact", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("Contacts") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_contacts_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.MagicSpell", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .HasConstraintName("fk_magic_spells_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Notification", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_notifications_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.NotificationPushSubscription", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_notification_push_subscriptions_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Profile", b => + { + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_account_profiles_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithOne("Profile") + .HasForeignKey("DysonNetwork.Sphere.Account.Profile", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_profiles_accounts_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_account_profiles_files_picture_id"); + + b.Navigation("Account"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Account.Relationship", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("OutgoingRelationships") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_relationships_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.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.Sphere.Activity.Activity", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_activities_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Auth.Challenge", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("Challenges") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_auth_challenges_accounts_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Auth.Session", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany("Sessions") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_auth_sessions_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Auth.Challenge", "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.Sphere.Chat.ChatMember", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_members_accounts_account_id"); + + 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("Account"); + + b.Navigation("ChatRoom"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_chat_rooms_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_chat_rooms_files_picture_id"); + + b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") + .WithMany("ChatRooms") + .HasForeignKey("RealmId") + .HasConstraintName("fk_chat_rooms_realms_realm_id"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + + 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_message_reaction_chat_messages_message_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_message_reaction_chat_members_sender_id"); + + b.Navigation("Message"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageStatus", b => + { + b.HasOne("DysonNetwork.Sphere.Chat.Message", "Message") + .WithMany("Statuses") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_statuses_chat_messages_message_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_chat_statuses_chat_members_sender_id"); + + b.Navigation("Message"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroupMember", b => + { + b.HasOne("DysonNetwork.Sphere.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.Sphere.Permission.PermissionNode", b => + { + b.HasOne("DysonNetwork.Sphere.Permission.PermissionGroup", "Group") + .WithMany("Nodes") + .HasForeignKey("GroupId") + .HasConstraintName("fk_permission_nodes_permission_groups_group_id"); + + b.Navigation("Group"); + }); + + 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.Post.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.HasOne("DysonNetwork.Sphere.Post.Post", "ThreadedPost") + .WithOne() + .HasForeignKey("DysonNetwork.Sphere.Post.Post", "ThreadedPostId") + .HasConstraintName("fk_posts_posts_threaded_post_id"); + + b.Navigation("ForwardedPost"); + + b.Navigation("Publisher"); + + b.Navigation("RepliedPost"); + + b.Navigation("ThreadedPost"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b => + { + b.HasOne("DysonNetwork.Sphere.Post.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.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_reactions_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Post", "Post") + .WithMany("Reactions") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_post_reactions_posts_post_id"); + + b.Navigation("Account"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .HasConstraintName("fk_publishers_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_publishers_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_publishers_files_picture_id"); + + b.Navigation("Account"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.PublisherMember", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publisher_members_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Publisher", "Publisher") + .WithMany("Members") + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publisher_members_publishers_publisher_id"); + + b.Navigation("Account"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_realms_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Background") + .WithMany() + .HasForeignKey("BackgroundId") + .HasConstraintName("fk_realms_files_background_id"); + + b.HasOne("DysonNetwork.Sphere.Storage.CloudFile", "Picture") + .WithMany() + .HasForeignKey("PictureId") + .HasConstraintName("fk_realms_files_picture_id"); + + b.Navigation("Account"); + + b.Navigation("Background"); + + b.Navigation("Picture"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.RealmMember", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_realm_members_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Realm.Realm", "Realm") + .WithMany("Members") + .HasForeignKey("RealmId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_realm_members_realms_realm_id"); + + b.Navigation("Account"); + + b.Navigation("Realm"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b => + { + b.HasOne("DysonNetwork.Sphere.Account.Account", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_files_accounts_account_id"); + + b.HasOne("DysonNetwork.Sphere.Chat.Message", null) + .WithMany("Attachments") + .HasForeignKey("MessageId") + .HasConstraintName("fk_files_chat_messages_message_id"); + + b.HasOne("DysonNetwork.Sphere.Post.Post", null) + .WithMany("Attachments") + .HasForeignKey("PostId") + .HasConstraintName("fk_files_posts_post_id"); + + b.Navigation("Account"); + }); + + 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.Account.Account", b => + { + b.Navigation("AuthFactors"); + + b.Navigation("Challenges"); + + b.Navigation("Contacts"); + + b.Navigation("IncomingRelationships"); + + b.Navigation("OutgoingRelationships"); + + b.Navigation("Profile") + .IsRequired(); + + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b => + { + b.Navigation("Members"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b => + { + b.Navigation("Attachments"); + + b.Navigation("Reactions"); + + b.Navigation("Statuses"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b => + { + b.Navigation("Members"); + + b.Navigation("Nodes"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Post", b => + { + b.Navigation("Attachments"); + + b.Navigation("Reactions"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b => + { + b.Navigation("Collections"); + + b.Navigation("Members"); + + b.Navigation("Posts"); + }); + + modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b => + { + b.Navigation("ChatRooms"); + + b.Navigation("Members"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DysonNetwork.Sphere/Permission/PermissionService.cs b/DysonNetwork.Sphere/Permission/PermissionService.cs index 1cbef7a..e0bf4ef 100644 --- a/DysonNetwork.Sphere/Permission/PermissionService.cs +++ b/DysonNetwork.Sphere/Permission/PermissionService.cs @@ -1,11 +1,23 @@ +using Microsoft.Extensions.Caching.Memory; using System.Text.Json; using Microsoft.EntityFrameworkCore; using NodaTime; namespace DysonNetwork.Sphere.Permission; -public class PermissionService(AppDatabase db) +public class PermissionService( + AppDatabase db, + IMemoryCache cache, + ILogger logger) { + private static readonly TimeSpan CacheExpiration = TimeSpan.FromMinutes(1); + + private string GetPermissionCacheKey(string actor, string area, string key) => + $"perm:{actor}:{area}:{key}"; + + private string GetGroupsCacheKey(string actor) => + $"perm_groups:{actor}"; + public async Task HasPermissionAsync(string actor, string area, string key) { var value = await GetPermissionAsync(actor, area, key); @@ -14,13 +26,27 @@ public class PermissionService(AppDatabase db) public async Task GetPermissionAsync(string actor, string area, string key) { + var cacheKey = GetPermissionCacheKey(actor, area, key); + + if (cache.TryGetValue(cacheKey, out var cachedValue)) + { + return cachedValue; + } + var now = SystemClock.Instance.GetCurrentInstant(); - var groupsId = await db.PermissionGroupMembers - .Where(n => n.Actor == actor) - .Where(n => n.ExpiredAt == null || n.ExpiredAt < now) - .Where(n => n.AffectedAt == null || n.AffectedAt >= now) - .Select(e => e.GroupId) - .ToListAsync(); + var groupsKey = GetGroupsCacheKey(actor); + + var groupsId = await cache.GetOrCreateAsync(groupsKey, async entry => + { + entry.AbsoluteExpirationRelativeToNow = CacheExpiration; + return await db.PermissionGroupMembers + .Where(n => n.Actor == actor) + .Where(n => n.ExpiredAt == null || n.ExpiredAt < now) + .Where(n => n.AffectedAt == null || n.AffectedAt >= now) + .Select(e => e.GroupId) + .ToListAsync(); + }); + var permission = await db.PermissionNodes .Where(n => n.GroupId == null || groupsId.Contains(n.GroupId.Value)) .Where(n => (n.Key == key || n.Key == "*") && (n.GroupId != null || n.Actor == actor) && n.Area == area) @@ -28,7 +54,11 @@ public class PermissionService(AppDatabase db) .Where(n => n.AffectedAt == null || n.AffectedAt >= now) .FirstOrDefaultAsync(); - return permission is not null ? _DeserializePermissionValue(permission.Value) : default; + var result = permission is not null ? _DeserializePermissionValue(permission.Value) : default; + + cache.Set(cacheKey, result, CacheExpiration); + + return result; } public async Task AddPermissionNode( @@ -55,6 +85,9 @@ public class PermissionService(AppDatabase db) db.PermissionNodes.Add(node); await db.SaveChangesAsync(); + // Invalidate related caches + InvalidatePermissionCache(actor, area, key); + return node; } @@ -85,6 +118,10 @@ public class PermissionService(AppDatabase db) db.PermissionNodes.Add(node); await db.SaveChangesAsync(); + // Invalidate related caches + InvalidatePermissionCache(actor, area, key); + cache.Remove(GetGroupsCacheKey(actor)); + return node; } @@ -95,6 +132,9 @@ public class PermissionService(AppDatabase db) .FirstOrDefaultAsync(); if (node is not null) db.PermissionNodes.Remove(node); await db.SaveChangesAsync(); + + // Invalidate cache + InvalidatePermissionCache(actor, area, key); } public async Task RemovePermissionNodeFromGroup(PermissionGroup group, string actor, string area, string key) @@ -106,6 +146,16 @@ public class PermissionService(AppDatabase db) if (node is null) return; db.PermissionNodes.Remove(node); await db.SaveChangesAsync(); + + // Invalidate caches + InvalidatePermissionCache(actor, area, key); + cache.Remove(GetGroupsCacheKey(actor)); + } + + private void InvalidatePermissionCache(string actor, string area, string key) + { + var cacheKey = GetPermissionCacheKey(actor, area, key); + cache.Remove(cacheKey); } private static T? _DeserializePermissionValue(JsonDocument json) diff --git a/DysonNetwork.Sphere/Post/PostController.cs b/DysonNetwork.Sphere/Post/PostController.cs index 6903187..59e85b0 100644 --- a/DysonNetwork.Sphere/Post/PostController.cs +++ b/DysonNetwork.Sphere/Post/PostController.cs @@ -85,8 +85,6 @@ public class PostController(AppDatabase db, PostService ps, RelationshipService var posts = await db.Posts .Where(e => e.RepliedPostId == id) .Include(e => e.Publisher) - .Include(e => e.Publisher.Picture) - .Include(e => e.Publisher.Background) .Include(e => e.ThreadedPost) .Include(e => e.ForwardedPost) .Include(e => e.Attachments) diff --git a/DysonNetwork.Sphere/Program.cs b/DysonNetwork.Sphere/Program.cs index 68c94a5..f557bf4 100644 --- a/DysonNetwork.Sphere/Program.cs +++ b/DysonNetwork.Sphere/Program.cs @@ -25,8 +25,10 @@ using NodaTime; using NodaTime.Serialization.SystemTextJson; using Quartz; using tusdotnet; +using tusdotnet.Interfaces; using tusdotnet.Models; using tusdotnet.Models.Configuration; +using tusdotnet.Stores; using File = System.IO.File; var builder = WebApplication.CreateBuilder(args); @@ -116,6 +118,11 @@ builder.Services.AddSwaggerGen(options => }); builder.Services.AddOpenApi(); +var tusDiskStore = new TusDiskStore( + builder.Configuration.GetSection("Tus").GetValue("StorePath")! +); +builder.Services.AddSingleton(tusDiskStore); + // The handlers for websocket builder.Services.AddScoped(); @@ -196,9 +203,6 @@ app.MapControllers().RequireRateLimiting("fixed"); app.MapStaticAssets().RequireRateLimiting("fixed"); app.MapRazorPages().RequireRateLimiting("fixed"); -var tusDiskStore = new tusdotnet.Stores.TusDiskStore( - builder.Configuration.GetSection("Tus").GetValue("StorePath")! -); app.MapTus("/files/tus", _ => Task.FromResult(new() { Store = tusDiskStore, @@ -216,8 +220,7 @@ app.MapTus("/files/tus", _ => Task.FromResult(new() } var httpContext = eventContext.HttpContext; - if (httpContext.Items["CurrentUser"] is Account user) - if (user is null) + if (httpContext.Items["CurrentUser"] is not Account user) { eventContext.FailRequest(HttpStatusCode.Unauthorized); return; @@ -226,7 +229,8 @@ app.MapTus("/files/tus", _ => Task.FromResult(new() var userId = httpContext.User.FindFirst("user_id")?.Value; if (userId == null) return; - var pm = httpContext.RequestServices.GetRequiredService(); + using var scope = httpContext.RequestServices.CreateScope(); + var pm = scope.ServiceProvider.GetRequiredService(); var allowed = await pm.HasPermissionAsync($"user:{userId}", "global", "files.create"); if (!allowed) { @@ -235,24 +239,37 @@ app.MapTus("/files/tus", _ => Task.FromResult(new() }, OnFileCompleteAsync = async eventContext => { - var httpContext = eventContext.HttpContext; - if (httpContext.Items["CurrentUser"] is not Account user) return; + using var scope = eventContext.HttpContext.RequestServices.CreateScope(); + var services = scope.ServiceProvider; + + try + { + var httpContext = eventContext.HttpContext; + if (httpContext.Items["CurrentUser"] is not Account user) return; - var file = await eventContext.GetFileAsync(); - var metadata = await file.GetMetadataAsync(eventContext.CancellationToken); - var fileName = metadata.TryGetValue("filename", out var fn) ? fn.GetString(Encoding.UTF8) : "uploaded_file"; - var contentType = metadata.TryGetValue("content-type", out var ct) ? ct.GetString(Encoding.UTF8) : null; - var fileStream = await file.GetContentAsync(eventContext.CancellationToken); + var file = await eventContext.GetFileAsync(); + var metadata = await file.GetMetadataAsync(eventContext.CancellationToken); + var fileName = metadata.TryGetValue("filename", out var fn) ? fn.GetString(Encoding.UTF8) : "uploaded_file"; + var contentType = metadata.TryGetValue("content-type", out var ct) ? ct.GetString(Encoding.UTF8) : null; + + var fileStream = await file.GetContentAsync(eventContext.CancellationToken); + + var fileService = services.GetRequiredService(); + var info = await fileService.ProcessNewFileAsync(user, file.Id, fileStream, fileName, contentType); - var fileService = eventContext.HttpContext.RequestServices.GetRequiredService(); + using var finalScope = eventContext.HttpContext.RequestServices.CreateScope(); + var jsonOptions = finalScope.ServiceProvider.GetRequiredService>().Value.JsonSerializerOptions; + var infoJson = JsonSerializer.Serialize(info, jsonOptions); + eventContext.HttpContext.Response.Headers.Append("X-FileInfo", infoJson); - var info = await fileService.ProcessNewFileAsync(user, file.Id, fileStream, fileName, contentType); - - var jsonOptions = httpContext.RequestServices.GetRequiredService>().Value - .JsonSerializerOptions; - var infoJson = JsonSerializer.Serialize(info, jsonOptions); - eventContext.HttpContext.Response.Headers.Append("X-FileInfo", infoJson); - }, + // Dispose the stream after all processing is complete + await fileStream.DisposeAsync(); + } + catch (Exception ex) + { + throw; + } + } } })); diff --git a/DysonNetwork.Sphere/Storage/FileService.cs b/DysonNetwork.Sphere/Storage/FileService.cs index bb155c0..fec6590 100644 --- a/DysonNetwork.Sphere/Storage/FileService.cs +++ b/DysonNetwork.Sphere/Storage/FileService.cs @@ -4,17 +4,23 @@ using System.Security.Cryptography; using Blurhash.ImageSharp; using Microsoft.EntityFrameworkCore; using Minio; -using Minio.DataModel; using Minio.DataModel.Args; using NodaTime; using Quartz; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using tusdotnet.Stores; using ExifTag = SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag; namespace DysonNetwork.Sphere.Storage; -public class FileService(AppDatabase db, IConfiguration configuration, ILogger logger, IServiceScopeFactory scopeFactory) +public class FileService( + AppDatabase db, + IConfiguration configuration, + TusDiskStore store, + ILogger logger, + IServiceScopeFactory scopeFactory +) { private static readonly string TempFilePrefix = "dyn-cloudfile"; @@ -28,17 +34,12 @@ public class FileService(AppDatabase db, IConfiguration configuration, ILogger(); + var result = new List<(string filePath, string suffix)>(); var fileSize = stream.Length; var hash = await HashFileAsync(stream, fileSize: fileSize); contentType ??= !fileName.Contains('.') ? "application/octet-stream" : MimeTypes.GetMimeType(fileName); - var existingFile = await db.Files.Where(f => f.Hash == hash).FirstOrDefaultAsync(); - if (existingFile is not null) return existingFile; - var file = new CloudFile { Id = fileId, @@ -53,17 +54,28 @@ public class FileService(AppDatabase db, IConfiguration configuration, ILogger(stream)) + // We still need ImageSharp for blurhash calculation + using (var imageSharp = await SixLabors.ImageSharp.Image.LoadAsync(stream)) { - var width = imageSharp.Width; - var height = imageSharp.Height; var blurhash = Blurhasher.Encode(imageSharp, 3, 3); - var format = imageSharp.Metadata.DecodedImageFormat?.Name ?? "unknown"; - var exifProfile = imageSharp.Metadata.ExifProfile; + // Reset stream position after ImageSharp read + stream.Position = 0; + + // Use NetVips for the rest + using var vipsImage = NetVips.Image.NewFromStream(stream); + + var width = vipsImage.Width; + var height = vipsImage.Height; + var format = vipsImage.Get("vips-loader") ?? "unknown"; + + // Try to get orientation from exif data ushort orientation = 1; List exif = []; + // NetVips supports reading exif with vipsImage.GetField("exif-ifd0-Orientation") + // but we'll keep the ImageSharp exif handling for now + var exifProfile = imageSharp.Metadata.ExifProfile; if (exifProfile?.Values.FirstOrDefault(e => e.Tag == ExifTag.Orientation) ?.GetValue() is ushort o) orientation = o; @@ -117,7 +129,7 @@ public class FileService(AppDatabase db, IConfiguration configuration, ILogger(); - + try { logger.LogInformation("Processed file {fileId}, now trying optimizing if possible...", fileId); @@ -125,40 +137,53 @@ public class FileService(AppDatabase db, IConfiguration configuration, ILogger tasks = []; var ogFilePath = Path.Join(configuration.GetValue("Tus:StorePath"), file.Id); - using var imageSharp = await Image.LoadAsync(ogFilePath); + var vipsImage = NetVips.Image.NewFromFile(ogFilePath); var imagePath = Path.Join(Path.GetTempPath(), $"{TempFilePrefix}#{file.Id}"); - tasks.Add(imageSharp.SaveAsWebpAsync(imagePath)); - modifiedResult.Add((imagePath, string.Empty)); + tasks.Add(Task.Run(() => vipsImage.WriteToFile(imagePath + ".webp"))); + result.Add((imagePath + ".webp", string.Empty)); - if (imageSharp.Size.Width * imageSharp.Size.Height >= 1024 * 1024) + if (vipsImage.Width * vipsImage.Height >= 1024 * 1024) { - var compressedClone = imageSharp.Clone(); - compressedClone.Mutate(i => i.Resize(new ResizeOptions - { - Mode = ResizeMode.Max, - Size = new Size(1024, 1024), - }) - ); + var scale = 1024.0 / Math.Max(vipsImage.Width, vipsImage.Height); var imageCompressedPath = Path.Join(Path.GetTempPath(), $"{TempFilePrefix}#{file.Id}-compressed"); - tasks.Add(compressedClone.SaveAsWebpAsync(imageCompressedPath)); - modifiedResult.Add((imageCompressedPath, ".compressed")); + + // Create and save image within the same synchronous block to avoid disposal issues + tasks.Add(Task.Run(() => { + using var compressedImage = vipsImage.Resize(scale); + compressedImage.WriteToFile(imageCompressedPath + ".webp"); + vipsImage.Dispose(); + })); + + result.Add((imageCompressedPath + ".webp", ".compressed")); file.HasCompression = true; } + else + { + vipsImage.Dispose(); + } await Task.WhenAll(tasks); } + else + { + var tempFilePath = Path.Join(Path.GetTempPath(), $"{TempFilePrefix}#{file.Id}"); + await using var fileStream = File.Create(tempFilePath); + stream.Position = 0; + await stream.CopyToAsync(fileStream); + result.Add((tempFilePath, string.Empty)); + } logger.LogInformation("Optimized file {fileId}, now uploading...", fileId); - if (modifiedResult.Count > 0) + if (result.Count > 0) { List> tasks = []; - tasks.AddRange(modifiedResult.Select(result => + tasks.AddRange(result.Select(result => nfs.UploadFileToRemoteAsync(file, result.filePath, null, result.suffix, true))); await Task.WhenAll(tasks); @@ -183,7 +208,10 @@ public class FileService(AppDatabase db, IConfiguration configuration, ILoggerForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -40,13 +43,18 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded \ No newline at end of file