♻️ Move the realm service from sphere to the pass
This commit is contained in:
@@ -19,7 +19,7 @@ public class BotAccountController(
|
||||
DeveloperService ds,
|
||||
DevProjectService projectService,
|
||||
ILogger<BotAccountController> logger,
|
||||
AccountClientHelper accounts,
|
||||
RemoteAccountService remoteAccounts,
|
||||
BotAccountReceiverService.BotAccountReceiverServiceClient accountsReceiver
|
||||
)
|
||||
: ControllerBase
|
||||
@@ -222,7 +222,7 @@ public class BotAccountController(
|
||||
if (bot is null || bot.ProjectId != projectId)
|
||||
return NotFound("Bot not found");
|
||||
|
||||
var botAccount = await accounts.GetBotAccount(bot.Id);
|
||||
var botAccount = await remoteAccounts.GetBotAccount(bot.Id);
|
||||
|
||||
if (request.Name is not null) botAccount.Name = request.Name;
|
||||
if (request.Nick is not null) botAccount.Nick = request.Nick;
|
||||
|
@@ -10,7 +10,7 @@ namespace DysonNetwork.Develop.Identity;
|
||||
public class BotAccountService(
|
||||
AppDatabase db,
|
||||
BotAccountReceiverService.BotAccountReceiverServiceClient accountReceiver,
|
||||
AccountClientHelper accounts
|
||||
RemoteAccountService remoteAccounts
|
||||
)
|
||||
{
|
||||
public async Task<SnBotAccount?> GetBotByIdAsync(Guid id)
|
||||
@@ -158,7 +158,7 @@ public class BotAccountService(
|
||||
public async Task<List<SnBotAccount>> LoadBotsAccountAsync(List<SnBotAccount> bots)
|
||||
{
|
||||
var automatedIds = bots.Select(b => b.Id).ToList();
|
||||
var data = await accounts.GetBotAccountBatch(automatedIds);
|
||||
var data = await remoteAccounts.GetBotAccountBatch(automatedIds);
|
||||
|
||||
foreach (var bot in bots)
|
||||
{
|
||||
|
@@ -39,6 +39,9 @@ public class AppDatabase(
|
||||
public DbSet<SnAuthClient> AuthClients { get; set; } = null!;
|
||||
public DbSet<SnApiKey> ApiKeys { get; set; } = null!;
|
||||
|
||||
public DbSet<SnRealm> Realms { get; set; } = null!;
|
||||
public DbSet<SnRealmMember> RealmMembers { get; set; } = null!;
|
||||
|
||||
public DbSet<SnWallet> Wallets { get; set; } = null!;
|
||||
public DbSet<SnWalletPocket> WalletPockets { get; set; } = null!;
|
||||
public DbSet<SnWalletOrder> PaymentOrders { get; set; } = null!;
|
||||
@@ -128,6 +131,14 @@ public class AppDatabase(
|
||||
.WithMany(a => a.IncomingRelationships)
|
||||
.HasForeignKey(r => r.RelatedId);
|
||||
|
||||
modelBuilder.Entity<SnRealmMember>()
|
||||
.HasKey(pm => new { pm.RealmId, pm.AccountId });
|
||||
modelBuilder.Entity<SnRealmMember>()
|
||||
.HasOne(pm => pm.Realm)
|
||||
.WithMany(p => p.Members)
|
||||
.HasForeignKey(pm => pm.RealmId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Automatically apply soft-delete filter to all entities inheriting BaseModel
|
||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||
{
|
||||
|
2677
DysonNetwork.Pass/Migrations/20251021153439_AddRealmFromSphere.Designer.cs
generated
Normal file
2677
DysonNetwork.Pass/Migrations/20251021153439_AddRealmFromSphere.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DysonNetwork.Pass.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddRealmFromSphere : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "realms",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
slug = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||
name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: false),
|
||||
is_community = table.Column<bool>(type: "boolean", nullable: false),
|
||||
is_public = table.Column<bool>(type: "boolean", nullable: false),
|
||||
picture_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
background_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
picture = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||
background = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||
verification = table.Column<SnVerificationMark>(type: "jsonb", nullable: true),
|
||||
account_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_realms", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "realm_members",
|
||||
columns: table => new
|
||||
{
|
||||
realm_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
account_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
role = table.Column<int>(type: "integer", nullable: false),
|
||||
joined_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||
leave_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
deleted_at = table.Column<Instant>(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_realms_realm_id",
|
||||
column: x => x.realm_id,
|
||||
principalTable: "realms",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "sn_chat_room",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true),
|
||||
type = table.Column<int>(type: "integer", nullable: false),
|
||||
is_community = table.Column<bool>(type: "boolean", nullable: false),
|
||||
is_public = table.Column<bool>(type: "boolean", nullable: false),
|
||||
picture_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
background_id = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
picture = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||
background = table.Column<SnCloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||
realm_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
sn_realm_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_sn_chat_room", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "fk_sn_chat_room_realms_sn_realm_id",
|
||||
column: x => x.sn_realm_id,
|
||||
principalTable: "realms",
|
||||
principalColumn: "id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "sn_chat_member",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
chat_room_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
account_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
nick = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||
role = table.Column<int>(type: "integer", nullable: false),
|
||||
notify = table.Column<int>(type: "integer", nullable: false),
|
||||
last_read_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||
joined_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||
leave_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||
is_bot = table.Column<bool>(type: "boolean", nullable: false),
|
||||
break_until = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||
timeout_until = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||
timeout_cause = table.Column<ChatTimeoutCause>(type: "jsonb", nullable: true),
|
||||
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_sn_chat_member", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "fk_sn_chat_member_sn_chat_room_chat_room_id",
|
||||
column: x => x.chat_room_id,
|
||||
principalTable: "sn_chat_room",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_realms_slug",
|
||||
table: "realms",
|
||||
column: "slug",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_sn_chat_member_chat_room_id",
|
||||
table: "sn_chat_member",
|
||||
column: "chat_room_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_sn_chat_room_sn_realm_id",
|
||||
table: "sn_chat_room",
|
||||
column: "sn_realm_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "realm_members");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "sn_chat_member");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "sn_chat_room");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "realms");
|
||||
}
|
||||
}
|
||||
}
|
@@ -954,6 +954,159 @@ namespace DysonNetwork.Pass.Migrations
|
||||
b.ToTable("auth_sessions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnChatMember", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<Guid>("AccountId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("account_id");
|
||||
|
||||
b.Property<Instant?>("BreakUntil")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("break_until");
|
||||
|
||||
b.Property<Guid>("ChatRoomId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("chat_room_id");
|
||||
|
||||
b.Property<Instant>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Instant?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<bool>("IsBot")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_bot");
|
||||
|
||||
b.Property<Instant?>("JoinedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("joined_at");
|
||||
|
||||
b.Property<Instant?>("LastReadAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_read_at");
|
||||
|
||||
b.Property<Instant?>("LeaveAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("leave_at");
|
||||
|
||||
b.Property<string>("Nick")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("nick");
|
||||
|
||||
b.Property<int>("Notify")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("notify");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("role");
|
||||
|
||||
b.Property<ChatTimeoutCause>("TimeoutCause")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("timeout_cause");
|
||||
|
||||
b.Property<Instant?>("TimeoutUntil")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("timeout_until");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_sn_chat_member");
|
||||
|
||||
b.HasIndex("ChatRoomId")
|
||||
.HasDatabaseName("ix_sn_chat_member_chat_room_id");
|
||||
|
||||
b.ToTable("sn_chat_member", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnChatRoom", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<SnCloudFileReferenceObject>("Background")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("background");
|
||||
|
||||
b.Property<string>("BackgroundId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("background_id");
|
||||
|
||||
b.Property<Instant>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Instant?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<bool>("IsCommunity")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_community");
|
||||
|
||||
b.Property<bool>("IsPublic")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_public");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<SnCloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
||||
b.Property<string>("PictureId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("picture_id");
|
||||
|
||||
b.Property<Guid?>("RealmId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("realm_id");
|
||||
|
||||
b.Property<Guid?>("SnRealmId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("sn_realm_id");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("type");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_sn_chat_room");
|
||||
|
||||
b.HasIndex("SnRealmId")
|
||||
.HasDatabaseName("ix_sn_chat_room_sn_realm_id");
|
||||
|
||||
b.ToTable("sn_chat_room", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCheckInResult", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -1252,6 +1405,127 @@ namespace DysonNetwork.Pass.Migrations
|
||||
b.ToTable("permission_nodes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnRealm", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<Guid>("AccountId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("account_id");
|
||||
|
||||
b.Property<SnCloudFileReferenceObject>("Background")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("background");
|
||||
|
||||
b.Property<string>("BackgroundId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("background_id");
|
||||
|
||||
b.Property<Instant>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Instant?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<bool>("IsCommunity")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_community");
|
||||
|
||||
b.Property<bool>("IsPublic")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_public");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<SnCloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
||||
b.Property<string>("PictureId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("picture_id");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("slug");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.Property<SnVerificationMark>("Verification")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("verification");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_realms");
|
||||
|
||||
b.HasIndex("Slug")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_realms_slug");
|
||||
|
||||
b.ToTable("realms", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnRealmMember", b =>
|
||||
{
|
||||
b.Property<Guid>("RealmId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("realm_id");
|
||||
|
||||
b.Property<Guid>("AccountId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("account_id");
|
||||
|
||||
b.Property<Instant>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at");
|
||||
|
||||
b.Property<Instant?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("deleted_at");
|
||||
|
||||
b.Property<Instant?>("JoinedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("joined_at");
|
||||
|
||||
b.Property<Instant?>("LeaveAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("leave_at");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("role");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("RealmId", "AccountId")
|
||||
.HasName("pk_realm_members");
|
||||
|
||||
b.ToTable("realm_members", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnSocialCreditRecord", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -2089,6 +2363,26 @@ namespace DysonNetwork.Pass.Migrations
|
||||
b.Navigation("Challenge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnChatMember", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnChatRoom", "ChatRoom")
|
||||
.WithMany("Members")
|
||||
.HasForeignKey("ChatRoomId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_sn_chat_member_sn_chat_room_chat_room_id");
|
||||
|
||||
b.Navigation("ChatRoom");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnChatRoom", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnRealm", null)
|
||||
.WithMany("ChatRooms")
|
||||
.HasForeignKey("SnRealmId")
|
||||
.HasConstraintName("fk_sn_chat_room_realms_sn_realm_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCheckInResult", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnAccount", "Account")
|
||||
@@ -2145,6 +2439,18 @@ namespace DysonNetwork.Pass.Migrations
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnRealmMember", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnRealm", "Realm")
|
||||
.WithMany("Members")
|
||||
.HasForeignKey("RealmId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_realm_members_realms_realm_id");
|
||||
|
||||
b.Navigation("Realm");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnSocialCreditRecord", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnAccount", "Account")
|
||||
@@ -2329,6 +2635,11 @@ namespace DysonNetwork.Pass.Migrations
|
||||
b.Navigation("Sessions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnChatRoom", b =>
|
||||
{
|
||||
b.Navigation("Members");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnPermissionGroup", b =>
|
||||
{
|
||||
b.Navigation("Members");
|
||||
@@ -2336,6 +2647,13 @@ namespace DysonNetwork.Pass.Migrations
|
||||
b.Navigation("Nodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnRealm", b =>
|
||||
{
|
||||
b.Navigation("ChatRooms");
|
||||
|
||||
b.Navigation("Members");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnWallet", b =>
|
||||
{
|
||||
b.Navigation("Pockets");
|
||||
|
@@ -1,14 +1,14 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
namespace DysonNetwork.Sphere.Realm;
|
||||
namespace DysonNetwork.Pass.Realm;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/realms")]
|
||||
@@ -19,7 +19,7 @@ public class RealmController(
|
||||
FileReferenceService.FileReferenceServiceClient fileRefs,
|
||||
ActionLogService.ActionLogServiceClient als,
|
||||
AccountService.AccountServiceClient accounts,
|
||||
AccountClientHelper accountsHelper
|
||||
RemoteAccountService remoteAccountsHelper
|
||||
) : Controller
|
||||
{
|
||||
[HttpGet("{slug}")]
|
||||
@@ -37,7 +37,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<SnRealm>>> ListJoinedRealms()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var members = await db.RealmMembers
|
||||
@@ -54,7 +54,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<SnRealmMember>>> ListInvites()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var members = await db.RealmMembers
|
||||
@@ -77,7 +77,7 @@ public class RealmController(
|
||||
public async Task<ActionResult<SnRealmMember>> InviteMember(string slug,
|
||||
[FromBody] RealmMemberRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var relatedUser =
|
||||
@@ -168,7 +168,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnRealm>> AcceptMemberInvite(string slug)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var member = await db.RealmMembers
|
||||
@@ -202,7 +202,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult> DeclineMemberInvite(string slug)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var member = await db.RealmMembers
|
||||
@@ -248,7 +248,7 @@ public class RealmController(
|
||||
|
||||
if (!realm.IsPublic)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
if (!await rs.IsMemberWithRole(realm.Id, Guid.Parse(currentUser.Id), RealmMemberRole.Normal))
|
||||
return StatusCode(403, "You must be a member to view this realm's members.");
|
||||
}
|
||||
@@ -263,7 +263,7 @@ public class RealmController(
|
||||
.OrderBy(m => m.JoinedAt)
|
||||
.ToListAsync();
|
||||
|
||||
var memberStatuses = await accountsHelper.GetAccountStatusBatch(
|
||||
var memberStatuses = await remoteAccountsHelper.GetAccountStatusBatch(
|
||||
members.Select(m => m.AccountId).ToList()
|
||||
);
|
||||
|
||||
@@ -306,7 +306,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnRealmMember>> GetCurrentIdentity(string slug)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var member = await db.RealmMembers
|
||||
@@ -323,7 +323,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult> LeaveRealm(string slug)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var member = await db.RealmMembers
|
||||
@@ -371,7 +371,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnRealm>> CreateRealm(RealmRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
if (string.IsNullOrWhiteSpace(request.Name)) return BadRequest("You cannot create a realm without a name.");
|
||||
if (string.IsNullOrWhiteSpace(request.Slug)) return BadRequest("You cannot create a realm without a slug.");
|
||||
|
||||
@@ -459,7 +459,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnRealm>> Update(string slug, [FromBody] RealmRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
|
||||
var realm = await db.Realms
|
||||
.Where(r => r.Slug == slug)
|
||||
@@ -568,7 +568,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SnRealmMember>> JoinRealm(string slug)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
|
||||
var realm = await db.Realms
|
||||
.Where(r => r.Slug == slug)
|
||||
@@ -641,7 +641,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult> RemoveMember(string slug, Guid memberId)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
|
||||
var realm = await db.Realms
|
||||
.Where(r => r.Slug == slug)
|
||||
@@ -681,7 +681,7 @@ public class RealmController(
|
||||
public async Task<ActionResult<SnRealmMember>> UpdateMemberRole(string slug, Guid memberId, [FromBody] int newRole)
|
||||
{
|
||||
if (newRole >= RealmMemberRole.Owner) return BadRequest("Unable to set realm member to owner or greater role.");
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
|
||||
var realm = await db.Realms
|
||||
.Where(r => r.Slug == slug)
|
||||
@@ -723,7 +723,7 @@ public class RealmController(
|
||||
[Authorize]
|
||||
public async Task<ActionResult> Delete(string slug)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser) return Unauthorized();
|
||||
|
||||
var transaction = await db.Database.BeginTransactionAsync();
|
||||
|
||||
@@ -737,11 +737,6 @@ public class RealmController(
|
||||
|
||||
try
|
||||
{
|
||||
var chats = await db.ChatRooms
|
||||
.Where(c => c.RealmId == realm.Id)
|
||||
.Select(c => c.Id)
|
||||
.ToListAsync();
|
||||
|
||||
db.Realms.Remove(realm);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
@@ -749,15 +744,6 @@ public class RealmController(
|
||||
await db.RealmMembers
|
||||
.Where(m => m.RealmId == realm.Id)
|
||||
.ExecuteUpdateAsync(m => m.SetProperty(m => m.DeletedAt, now));
|
||||
await db.ChatRooms
|
||||
.Where(c => c.RealmId == realm.Id)
|
||||
.ExecuteUpdateAsync(c => c.SetProperty(c => c.DeletedAt, now));
|
||||
await db.ChatMessages
|
||||
.Where(m => chats.Contains(m.ChatRoomId))
|
||||
.ExecuteUpdateAsync(m => m.SetProperty(m => m.DeletedAt, now));
|
||||
await db.ChatMembers
|
||||
.Where(m => chats.Contains(m.ChatRoomId))
|
||||
.ExecuteUpdateAsync(m => m.SetProperty(m => m.DeletedAt, now));
|
||||
await db.SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
}
|
@@ -1,20 +1,18 @@
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Shared;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using DysonNetwork.Sphere.Localization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace DysonNetwork.Sphere.Realm;
|
||||
namespace DysonNetwork.Pass.Realm;
|
||||
|
||||
public class RealmService(
|
||||
AppDatabase db,
|
||||
RingService.RingServiceClient pusher,
|
||||
AccountService.AccountServiceClient accounts,
|
||||
IStringLocalizer<NotificationResource> localizer,
|
||||
AccountClientHelper accountsHelper,
|
||||
ICacheService cache
|
||||
)
|
||||
{
|
||||
@@ -42,13 +40,18 @@ public class RealmService(
|
||||
|
||||
public async Task SendInviteNotify(SnRealmMember member)
|
||||
{
|
||||
var account = await accounts.GetAccountAsync(new GetAccountRequest { Id = member.AccountId.ToString() });
|
||||
CultureService.SetCultureInfo(account);
|
||||
var account = await db.Accounts
|
||||
.Include(a => a.Profile)
|
||||
.FirstOrDefaultAsync(a => a.Id == member.AccountId);
|
||||
|
||||
if (account == null) throw new InvalidOperationException("Account not found");
|
||||
|
||||
CultureService.SetCultureInfo(account.Language);
|
||||
|
||||
await pusher.SendPushNotificationToUserAsync(
|
||||
new SendPushNotificationToUserRequest
|
||||
{
|
||||
UserId = account.Id,
|
||||
UserId = account.Id.ToString(),
|
||||
Notification = new PushNotification
|
||||
{
|
||||
Topic = "invites.realms",
|
||||
@@ -75,20 +78,26 @@ public class RealmService(
|
||||
|
||||
public async Task<SnRealmMember> LoadMemberAccount(SnRealmMember member)
|
||||
{
|
||||
var account = await accountsHelper.GetAccount(member.AccountId);
|
||||
member.Account = SnAccount.FromProtoValue(account);
|
||||
var account = await db.Accounts
|
||||
.Include(a => a.Profile)
|
||||
.FirstOrDefaultAsync(a => a.Id == member.AccountId);
|
||||
if (account != null)
|
||||
member.Account = account;
|
||||
return member;
|
||||
}
|
||||
|
||||
public async Task<List<SnRealmMember>> LoadMemberAccounts(ICollection<SnRealmMember> members)
|
||||
{
|
||||
var accountIds = members.Select(m => m.AccountId).ToList();
|
||||
var accounts = (await accountsHelper.GetAccountBatch(accountIds)).ToDictionary(a => Guid.Parse(a.Id), a => a);
|
||||
var accountsDict = await db.Accounts
|
||||
.Include(a => a.Profile)
|
||||
.Where(a => accountIds.Contains(a.Id))
|
||||
.ToDictionaryAsync(a => a.Id, a => a);
|
||||
|
||||
return members.Select(m =>
|
||||
{
|
||||
if (accounts.TryGetValue(m.AccountId, out var account))
|
||||
m.Account = SnAccount.FromProtoValue(account);
|
||||
if (accountsDict.TryGetValue(m.AccountId, out var account))
|
||||
m.Account = account;
|
||||
return m;
|
||||
}).ToList();
|
||||
}
|
140
DysonNetwork.Pass/Realm/RealmServiceGrpc.cs
Normal file
140
DysonNetwork.Pass/Realm/RealmServiceGrpc.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Shared;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace DysonNetwork.Pass.Realm;
|
||||
|
||||
public class RealmServiceGrpc(
|
||||
AppDatabase db,
|
||||
RingService.RingServiceClient pusher,
|
||||
IStringLocalizer<NotificationResource> localizer,
|
||||
ICacheService cache
|
||||
)
|
||||
: Shared.Proto.RealmService.RealmServiceBase
|
||||
{
|
||||
private const string CacheKeyPrefix = "account:realms:";
|
||||
|
||||
public override async Task<Shared.Proto.Realm> GetRealm(GetRealmRequest request, ServerCallContext context)
|
||||
{
|
||||
var realm = request.QueryCase switch
|
||||
{
|
||||
GetRealmRequest.QueryOneofCase.Id when !string.IsNullOrWhiteSpace(request.Id) => await db.Realms.FindAsync(
|
||||
Guid.Parse(request.Id)),
|
||||
GetRealmRequest.QueryOneofCase.Slug when !string.IsNullOrWhiteSpace(request.Slug) => await db.Realms
|
||||
.FirstOrDefaultAsync(r => r.Slug == request.Slug),
|
||||
_ => throw new RpcException(new Status(StatusCode.InvalidArgument, "Must provide either id or slug"))
|
||||
};
|
||||
|
||||
return realm == null
|
||||
? throw new RpcException(new Status(StatusCode.NotFound, "Realm not found"))
|
||||
: realm.ToProtoValue();
|
||||
}
|
||||
|
||||
public override async Task<GetUserRealmsResponse> GetUserRealms(GetUserRealmsRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
var accountId = Guid.Parse(request.AccountId);
|
||||
var cacheKey = $"{CacheKeyPrefix}{accountId}";
|
||||
var (found, cachedRealms) = await cache.GetAsyncWithStatus<List<Guid>>(cacheKey);
|
||||
if (found && cachedRealms != null)
|
||||
return new GetUserRealmsResponse { RealmIds = { cachedRealms.Select(g => g.ToString()) } };
|
||||
|
||||
var realms = await db.RealmMembers
|
||||
.Include(m => m.Realm)
|
||||
.Where(m => m.AccountId == accountId)
|
||||
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||
.Select(m => m.Realm!.Id)
|
||||
.ToListAsync();
|
||||
|
||||
// Cache the result for 5 minutes
|
||||
await cache.SetAsync(cacheKey, realms, TimeSpan.FromMinutes(5));
|
||||
|
||||
return new GetUserRealmsResponse { RealmIds = { realms.Select(g => g.ToString()) } };
|
||||
}
|
||||
|
||||
public override async Task<Empty> SendInviteNotify(SendInviteNotifyRequest request, ServerCallContext context)
|
||||
{
|
||||
var member = request.Member;
|
||||
var account = await db.Accounts
|
||||
.AsNoTracking()
|
||||
.Include(a => a.Profile)
|
||||
.FirstOrDefaultAsync(a => a.Id == Guid.Parse(member.AccountId));
|
||||
|
||||
if (account == null) throw new RpcException(new Status(StatusCode.NotFound, "Account not found"));
|
||||
|
||||
CultureService.SetCultureInfo(account.Language);
|
||||
|
||||
await pusher.SendPushNotificationToUserAsync(
|
||||
new SendPushNotificationToUserRequest
|
||||
{
|
||||
UserId = account.Id.ToString(),
|
||||
Notification = new PushNotification
|
||||
{
|
||||
Topic = "invites.realms",
|
||||
Title = localizer["RealmInviteTitle"],
|
||||
Body = localizer["RealmInviteBody", member.Realm?.Name ?? "Unknown Realm"],
|
||||
ActionUri = "/realms",
|
||||
IsSavable = true
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return new Empty();
|
||||
}
|
||||
|
||||
public override async Task<BoolValue> IsMemberWithRole(IsMemberWithRoleRequest request, ServerCallContext context)
|
||||
{
|
||||
if (request.RequiredRoles.Count == 0)
|
||||
return new BoolValue { Value = false };
|
||||
|
||||
var maxRequiredRole = request.RequiredRoles.Max();
|
||||
var member = await db.RealmMembers
|
||||
.Where(m => m.RealmId == Guid.Parse(request.RealmId) && m.AccountId == Guid.Parse(request.AccountId) &&
|
||||
m.JoinedAt != null && m.LeaveAt == null)
|
||||
.FirstOrDefaultAsync();
|
||||
return new BoolValue { Value = member?.Role >= maxRequiredRole };
|
||||
}
|
||||
|
||||
public override async Task<RealmMember> LoadMemberAccount(LoadMemberAccountRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
var member = request.Member;
|
||||
var account = await db.Accounts
|
||||
.AsNoTracking()
|
||||
.Include(a => a.Profile)
|
||||
.FirstOrDefaultAsync(a => a.Id == Guid.Parse(member.AccountId));
|
||||
|
||||
var response = new RealmMember(member) { Account = account?.ToProtoValue() };
|
||||
return response;
|
||||
}
|
||||
|
||||
public override async Task<LoadMemberAccountsResponse> LoadMemberAccounts(LoadMemberAccountsRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
var accountIds = request.Members.Select(m => Guid.Parse(m.AccountId)).ToList();
|
||||
var accounts = await db.Accounts
|
||||
.AsNoTracking()
|
||||
.Include(a => a.Profile)
|
||||
.Where(a => accountIds.Contains(a.Id))
|
||||
.ToDictionaryAsync(a => a.Id, a => a.ToProtoValue());
|
||||
|
||||
var response = new LoadMemberAccountsResponse();
|
||||
foreach (var member in request.Members)
|
||||
{
|
||||
var updatedMember = new RealmMember(member);
|
||||
if (accounts.TryGetValue(Guid.Parse(member.AccountId), out var account))
|
||||
{
|
||||
updatedMember.Account = account;
|
||||
}
|
||||
|
||||
response.Members.Add(updatedMember);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
@@ -3,6 +3,7 @@ using DysonNetwork.Pass.Auth;
|
||||
using DysonNetwork.Pass.Credit;
|
||||
using DysonNetwork.Pass.Leveling;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Pass.Realm;
|
||||
using DysonNetwork.Pass.Wallet;
|
||||
using DysonNetwork.Shared.Http;
|
||||
using Prometheus;
|
||||
@@ -42,6 +43,7 @@ public static class ApplicationConfiguration
|
||||
app.MapGrpcService<BotAccountReceiverGrpc>();
|
||||
app.MapGrpcService<WalletServiceGrpc>();
|
||||
app.MapGrpcService<PaymentServiceGrpc>();
|
||||
app.MapGrpcService<RealmServiceGrpc>();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ using DysonNetwork.Pass.Credit;
|
||||
using DysonNetwork.Pass.Handlers;
|
||||
using DysonNetwork.Pass.Leveling;
|
||||
using DysonNetwork.Pass.Mailer;
|
||||
using DysonNetwork.Pass.Realm;
|
||||
using DysonNetwork.Pass.Safety;
|
||||
using DysonNetwork.Pass.Wallet.PaymentHandlers;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
@@ -152,6 +153,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<SafetyService>();
|
||||
services.AddScoped<SocialCreditService>();
|
||||
services.AddScoped<ExperienceService>();
|
||||
services.AddScoped<RealmService>();
|
||||
|
||||
services.Configure<OidcProviderOptions>(configuration.GetSection("OidcProvider"));
|
||||
services.AddScoped<OidcProviderService>();
|
||||
|
@@ -30,7 +30,7 @@ public class SnChatRoom : ModelBase, IIdentifiedResource
|
||||
[JsonIgnore] public ICollection<SnChatMember> Members { get; set; } = new List<SnChatMember>();
|
||||
|
||||
public Guid? RealmId { get; set; }
|
||||
public SnRealm? Realm { get; set; }
|
||||
[NotMapped] public SnRealm? Realm { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
[JsonPropertyName("members")]
|
||||
|
@@ -64,7 +64,7 @@ public class SnPost : ModelBase, IIdentifiedResource, IActivity
|
||||
public SnPost? ForwardedPost { get; set; }
|
||||
|
||||
public Guid? RealmId { get; set; }
|
||||
public SnRealm? Realm { get; set; }
|
||||
[NotMapped] public SnRealm? Realm { get; set; }
|
||||
|
||||
[Column(TypeName = "jsonb")] public List<SnCloudFileReferenceObject> Attachments { get; set; } = [];
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.Protobuf;
|
||||
|
||||
namespace DysonNetwork.Shared.Models;
|
||||
|
||||
@@ -31,6 +33,28 @@ public class SnRealm : ModelBase, IIdentifiedResource
|
||||
public Guid AccountId { get; set; }
|
||||
|
||||
public string ResourceIdentifier => $"realm:{Id}";
|
||||
|
||||
public Realm ToProtoValue()
|
||||
{
|
||||
return new Realm
|
||||
{
|
||||
Id = Id.ToString(),
|
||||
Name = Name
|
||||
};
|
||||
}
|
||||
|
||||
public static SnRealm FromProtoValue(Realm proto)
|
||||
{
|
||||
return new SnRealm
|
||||
{
|
||||
Id = Guid.Parse(proto.Id),
|
||||
Name = proto.Name,
|
||||
Slug = "", // Required but not in proto
|
||||
Description = "",
|
||||
IsCommunity = false,
|
||||
IsPublic = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class RealmMemberRole
|
||||
@@ -51,4 +75,40 @@ public class SnRealmMember : ModelBase
|
||||
public int Role { get; set; } = RealmMemberRole.Normal;
|
||||
public Instant? JoinedAt { get; set; }
|
||||
public Instant? LeaveAt { get; set; }
|
||||
|
||||
public Proto.RealmMember ToProtoValue()
|
||||
{
|
||||
var proto = new Proto.RealmMember
|
||||
{
|
||||
AccountId = AccountId.ToString(),
|
||||
RealmId = RealmId.ToString(),
|
||||
Role = Role,
|
||||
JoinedAt = JoinedAt?.ToTimestamp(),
|
||||
LeaveAt = LeaveAt?.ToTimestamp(),
|
||||
Realm = Realm.ToProtoValue()
|
||||
};
|
||||
if (Account != null)
|
||||
{
|
||||
proto.Account = Account.ToProtoValue();
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
public static SnRealmMember FromProtoValue(RealmMember proto)
|
||||
{
|
||||
var member = new SnRealmMember
|
||||
{
|
||||
AccountId = Guid.Parse(proto.AccountId),
|
||||
RealmId = Guid.Parse(proto.RealmId),
|
||||
Role = proto.Role,
|
||||
JoinedAt = proto.JoinedAt?.ToInstant(),
|
||||
LeaveAt = proto.LeaveAt?.ToInstant(),
|
||||
Realm = proto.Realm != null ? SnRealm.FromProtoValue(proto.Realm) : new SnRealm() // Provide default or handle null
|
||||
};
|
||||
if (proto.Account != null)
|
||||
{
|
||||
member.Account = SnAccount.FromProtoValue(proto.Account);
|
||||
}
|
||||
return member;
|
||||
}
|
||||
}
|
84
DysonNetwork.Shared/Proto/realm.proto
Normal file
84
DysonNetwork.Shared/Proto/realm.proto
Normal file
@@ -0,0 +1,84 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package proto;
|
||||
|
||||
option csharp_namespace = "DysonNetwork.Shared.Proto";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/wrappers.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
import 'account.proto';
|
||||
|
||||
// Message Definitions
|
||||
|
||||
message Realm {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
message RealmMember {
|
||||
string account_id = 1;
|
||||
string realm_id = 2;
|
||||
int32 role = 3;
|
||||
optional google.protobuf.Timestamp joined_at = 4;
|
||||
optional google.protobuf.Timestamp leave_at = 5;
|
||||
optional Account account = 6;
|
||||
optional Realm realm = 7;
|
||||
}
|
||||
|
||||
// Service Definitions
|
||||
|
||||
service RealmService {
|
||||
// Get realm by id or slug
|
||||
rpc GetRealm(GetRealmRequest) returns (Realm) {}
|
||||
// Get realms for a user
|
||||
rpc GetUserRealms(GetUserRealmsRequest) returns (GetUserRealmsResponse) {}
|
||||
// Send invitation notification
|
||||
rpc SendInviteNotify(SendInviteNotifyRequest) returns (google.protobuf.Empty) {}
|
||||
// Check if member has required role
|
||||
rpc IsMemberWithRole(IsMemberWithRoleRequest) returns (google.protobuf.BoolValue) {}
|
||||
// Load account for a member
|
||||
rpc LoadMemberAccount(LoadMemberAccountRequest) returns (RealmMember) {}
|
||||
// Load accounts for members
|
||||
rpc LoadMemberAccounts(LoadMemberAccountsRequest) returns (LoadMemberAccountsResponse) {}
|
||||
}
|
||||
|
||||
// Request/Response Messages
|
||||
|
||||
message GetRealmRequest {
|
||||
oneof query {
|
||||
string id = 1;
|
||||
string slug = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message GetUserRealmsRequest {
|
||||
string account_id = 1;
|
||||
}
|
||||
|
||||
message GetUserRealmsResponse {
|
||||
repeated string realm_ids = 1;
|
||||
}
|
||||
|
||||
message SendInviteNotifyRequest {
|
||||
RealmMember member = 1;
|
||||
}
|
||||
|
||||
message IsMemberWithRoleRequest {
|
||||
string realm_id = 1;
|
||||
string account_id = 2;
|
||||
repeated int32 required_roles = 3;
|
||||
}
|
||||
|
||||
message LoadMemberAccountRequest {
|
||||
RealmMember member = 1;
|
||||
}
|
||||
|
||||
message LoadMemberAccountsRequest {
|
||||
repeated RealmMember members = 1;
|
||||
}
|
||||
|
||||
message LoadMemberAccountsResponse {
|
||||
repeated RealmMember members = 1;
|
||||
}
|
@@ -3,7 +3,7 @@ using DysonNetwork.Shared.Proto;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public class AccountClientHelper(AccountService.AccountServiceClient accounts)
|
||||
public class RemoteAccountService(AccountService.AccountServiceClient accounts)
|
||||
{
|
||||
public async Task<Account> GetAccount(Guid id)
|
||||
{
|
60
DysonNetwork.Shared/Registry/RemoteRealmService.cs
Normal file
60
DysonNetwork.Shared/Registry/RemoteRealmService.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public class RemoteRealmService(RealmService.RealmServiceClient realms)
|
||||
{
|
||||
public async Task<SnRealm> GetRealm(string id)
|
||||
{
|
||||
var request = new GetRealmRequest { Id = id };
|
||||
var response = await realms.GetRealmAsync(request);
|
||||
return SnRealm.FromProtoValue(response);
|
||||
}
|
||||
|
||||
public async Task<SnRealm> GetRealmBySlug(string slug)
|
||||
{
|
||||
var request = new GetRealmRequest { Slug = slug };
|
||||
var response = await realms.GetRealmAsync(request);
|
||||
return SnRealm.FromProtoValue(response);
|
||||
}
|
||||
|
||||
public async Task<List<Guid>> GetUserRealms(Guid accountId)
|
||||
{
|
||||
var request = new GetUserRealmsRequest { AccountId = accountId.ToString() };
|
||||
var response = await realms.GetUserRealmsAsync(request);
|
||||
return response.RealmIds.Select(Guid.Parse).ToList();
|
||||
}
|
||||
|
||||
public async Task SendInviteNotify(SnRealmMember member)
|
||||
{
|
||||
var protoMember = member.ToProtoValue();
|
||||
var request = new SendInviteNotifyRequest { Member = protoMember };
|
||||
await realms.SendInviteNotifyAsync(request);
|
||||
}
|
||||
|
||||
public async Task<bool> IsMemberWithRole(Guid realmId, Guid accountId, List<int> requiredRoles)
|
||||
{
|
||||
var request = new IsMemberWithRoleRequest { RealmId = realmId.ToString(), AccountId = accountId.ToString() };
|
||||
request.RequiredRoles.AddRange(requiredRoles);
|
||||
var response = await realms.IsMemberWithRoleAsync(request);
|
||||
return response.Value;
|
||||
}
|
||||
|
||||
public async Task<SnRealmMember> LoadMemberAccount(SnRealmMember member)
|
||||
{
|
||||
var protoMember = member.ToProtoValue();
|
||||
var request = new LoadMemberAccountRequest { Member = protoMember };
|
||||
var response = await realms.LoadMemberAccountAsync(request);
|
||||
return SnRealmMember.FromProtoValue(response);
|
||||
}
|
||||
|
||||
public async Task<List<SnRealmMember>> LoadMemberAccounts(List<SnRealmMember> members)
|
||||
{
|
||||
var protoMembers = members.Select(m => m.ToProtoValue()).ToList();
|
||||
var request = new LoadMemberAccountsRequest();
|
||||
request.Members.AddRange(protoMembers);
|
||||
var response = await realms.LoadMemberAccountsAsync(request);
|
||||
return response.Members.Select(SnRealmMember.FromProtoValue).ToList();
|
||||
}
|
||||
}
|
@@ -36,11 +36,11 @@ public static class ServiceInjectionHelper
|
||||
public static IServiceCollection AddAccountService(this IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.AddGrpcClient<AccountService.AccountServiceClient>(o => o.Address = new Uri("https://_grpc.pass") )
|
||||
.AddGrpcClient<AccountService.AccountServiceClient>(o => o.Address = new Uri("https://_grpc.pass"))
|
||||
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler()
|
||||
{ ServerCertificateCustomValidationCallback = (_, _, _, _) => true }
|
||||
);
|
||||
services.AddSingleton<AccountClientHelper>();
|
||||
services.AddSingleton<RemoteAccountService>();
|
||||
|
||||
services
|
||||
.AddGrpcClient<BotAccountReceiverService.BotAccountReceiverServiceClient>(o =>
|
||||
@@ -60,6 +60,13 @@ public static class ServiceInjectionHelper
|
||||
{ ServerCertificateCustomValidationCallback = (_, _, _, _) => true }
|
||||
);
|
||||
|
||||
services
|
||||
.AddGrpcClient<RealmService.RealmServiceClient>(o => o.Address = new Uri("https://_grpc.pass"))
|
||||
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler()
|
||||
{ ServerCertificateCustomValidationCallback = (_, _, _, _) => true }
|
||||
);
|
||||
services.AddSingleton<RemoteRealmService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -70,7 +77,8 @@ public static class ServiceInjectionHelper
|
||||
{ ServerCertificateCustomValidationCallback = (_, _, _, _) => true }
|
||||
);
|
||||
|
||||
services.AddGrpcClient<FileReferenceService.FileReferenceServiceClient>(o => o.Address = new Uri("https://_grpc.drive"))
|
||||
services.AddGrpcClient<FileReferenceService.FileReferenceServiceClient>(o =>
|
||||
o.Address = new Uri("https://_grpc.drive"))
|
||||
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler()
|
||||
{ ServerCertificateCustomValidationCallback = (_, _, _, _) => true }
|
||||
);
|
||||
@@ -80,7 +88,8 @@ public static class ServiceInjectionHelper
|
||||
|
||||
public static IServiceCollection AddPublisherService(this IServiceCollection services)
|
||||
{
|
||||
services.AddGrpcClient<PublisherService.PublisherServiceClient>(o => o.Address = new Uri("https://_grpc.sphere"))
|
||||
services
|
||||
.AddGrpcClient<PublisherService.PublisherServiceClient>(o => o.Address = new Uri("https://_grpc.sphere"))
|
||||
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler()
|
||||
{ ServerCertificateCustomValidationCallback = (_, _, _, _) => true }
|
||||
);
|
||||
@@ -90,7 +99,8 @@ public static class ServiceInjectionHelper
|
||||
|
||||
public static IServiceCollection AddDevelopService(this IServiceCollection services)
|
||||
{
|
||||
services.AddGrpcClient<CustomAppService.CustomAppServiceClient>(o => o.Address = new Uri("https://_grpc.develop"))
|
||||
services.AddGrpcClient<CustomAppService.CustomAppServiceClient>(o =>
|
||||
o.Address = new Uri("https://_grpc.develop"))
|
||||
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler()
|
||||
{ ServerCertificateCustomValidationCallback = (_, _, _, _) => true }
|
||||
);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using DysonNetwork.Sphere.Discovery;
|
||||
using DysonNetwork.Sphere.Post;
|
||||
using DysonNetwork.Sphere.Realm;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
@@ -13,7 +13,7 @@ public class ActivityService(
|
||||
AppDatabase db,
|
||||
Publisher.PublisherService pub,
|
||||
PostService ps,
|
||||
RealmService rs,
|
||||
RemoteRealmService rs,
|
||||
DiscoveryService ds,
|
||||
AccountService.AccountServiceClient accounts
|
||||
)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
@@ -24,6 +25,10 @@ public class AppDatabase(
|
||||
public DbSet<SnPublisherSubscription> PublisherSubscriptions { get; set; } = null!;
|
||||
public DbSet<SnPublisherFeature> PublisherFeatures { get; set; } = null!;
|
||||
|
||||
// TODO Remove the realms table after the data migration is done
|
||||
public DbSet<SnRealm> Realms { get; set; } = null!;
|
||||
public DbSet<SnRealmMember> RealmMembers { get; set; } = null!;
|
||||
|
||||
public DbSet<SnPost> Posts { get; set; } = null!;
|
||||
public DbSet<SnPostReaction> PostReactions { get; set; } = null!;
|
||||
public DbSet<SnPostAward> PostAwards { get; set; } = null!;
|
||||
@@ -33,26 +38,23 @@ public class AppDatabase(
|
||||
public DbSet<SnPostFeaturedRecord> PostFeaturedRecords { get; set; } = null!;
|
||||
public DbSet<SnPostCategorySubscription> PostCategorySubscriptions { get; set; } = null!;
|
||||
|
||||
public DbSet<Shared.Models.SnPoll> Polls { get; set; } = null!;
|
||||
public DbSet<SnPoll> Polls { get; set; } = null!;
|
||||
public DbSet<SnPollQuestion> PollQuestions { get; set; } = null!;
|
||||
public DbSet<SnPollAnswer> PollAnswers { get; set; } = null!;
|
||||
|
||||
public DbSet<Shared.Models.SnRealm> Realms { get; set; } = null!;
|
||||
public DbSet<SnRealmMember> RealmMembers { get; set; } = null!;
|
||||
|
||||
public DbSet<SnChatRoom> ChatRooms { get; set; } = null!;
|
||||
public DbSet<SnChatMember> ChatMembers { get; set; } = null!;
|
||||
public DbSet<SnChatMessage> ChatMessages { get; set; } = null!;
|
||||
public DbSet<SnRealtimeCall> ChatRealtimeCall { get; set; } = null!;
|
||||
public DbSet<SnChatMessageReaction> ChatReactions { get; set; } = null!;
|
||||
|
||||
public DbSet<Shared.Models.SnSticker> Stickers { get; set; } = null!;
|
||||
public DbSet<SnSticker> Stickers { get; set; } = null!;
|
||||
public DbSet<StickerPack> StickerPacks { get; set; } = null!;
|
||||
public DbSet<StickerPackOwnership> StickerPackOwnerships { get; set; } = null!;
|
||||
|
||||
public DbSet<WebReader.WebArticle> WebArticles { get; set; } = null!;
|
||||
public DbSet<WebReader.WebFeed> WebFeeds { get; set; } = null!;
|
||||
public DbSet<WebReader.WebFeedSubscription> WebFeedSubscriptions { get; set; } = null!;
|
||||
public DbSet<WebArticle> WebArticles { get; set; } = null!;
|
||||
public DbSet<WebFeed> WebFeeds { get; set; } = null!;
|
||||
public DbSet<WebFeedSubscription> WebFeedSubscriptions { get; set; } = null!;
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
@@ -150,10 +152,10 @@ public class AppDatabase(
|
||||
.HasForeignKey(m => m.SenderId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<WebReader.WebFeed>()
|
||||
modelBuilder.Entity<WebFeed>()
|
||||
.HasIndex(f => f.Url)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<WebReader.WebArticle>()
|
||||
modelBuilder.Entity<WebArticle>()
|
||||
.HasIndex(a => a.Url)
|
||||
.IsUnique();
|
||||
|
||||
|
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DysonNetwork.Sphere.Autocompletion;
|
||||
|
||||
public class AutocompletionService(AppDatabase db, AccountClientHelper accountsHelper)
|
||||
public class AutocompletionService(AppDatabase db, RemoteAccountService remoteAccountsHelper)
|
||||
{
|
||||
public async Task<List<DysonNetwork.Shared.Models.Autocompletion>> GetAutocompletion(string content, Guid? chatId = null, Guid? realmId = null, int limit = 10)
|
||||
{
|
||||
@@ -47,7 +47,7 @@ public class AutocompletionService(AppDatabase db, AccountClientHelper accountsH
|
||||
switch (type)
|
||||
{
|
||||
case "u":
|
||||
var allAccounts = await accountsHelper.SearchAccounts(query);
|
||||
var allAccounts = await remoteAccountsHelper.SearchAccounts(query);
|
||||
var filteredAccounts = allAccounts;
|
||||
|
||||
if (chatId.HasValue)
|
||||
|
@@ -6,7 +6,7 @@ using DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using DysonNetwork.Sphere.Localization;
|
||||
using DysonNetwork.Sphere.Realm;
|
||||
|
||||
using Grpc.Core;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Localization;
|
||||
@@ -20,14 +20,14 @@ namespace DysonNetwork.Sphere.Chat;
|
||||
public class ChatRoomController(
|
||||
AppDatabase db,
|
||||
ChatRoomService crs,
|
||||
RealmService rs,
|
||||
RemoteRealmService rs,
|
||||
IStringLocalizer<NotificationResource> localizer,
|
||||
AccountService.AccountServiceClient accounts,
|
||||
FileService.FileServiceClient files,
|
||||
FileReferenceService.FileReferenceServiceClient fileRefs,
|
||||
ActionLogService.ActionLogServiceClient als,
|
||||
RingService.RingServiceClient pusher,
|
||||
AccountClientHelper accountsHelper
|
||||
RemoteAccountService remoteAccountsHelper
|
||||
) : ControllerBase
|
||||
{
|
||||
[HttpGet("{id:guid}")]
|
||||
@@ -203,7 +203,7 @@ public class ChatRoomController(
|
||||
if (request.RealmId is not null)
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(request.RealmId.Value, Guid.Parse(currentUser.Id),
|
||||
RealmMemberRole.Moderator))
|
||||
[RealmMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a moderator to create chat linked to the realm.");
|
||||
chatRoom.RealmId = request.RealmId;
|
||||
}
|
||||
@@ -301,7 +301,7 @@ public class ChatRoomController(
|
||||
if (chatRoom.RealmId is not null)
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(chatRoom.RealmId.Value, Guid.Parse(currentUser.Id),
|
||||
RealmMemberRole.Moderator))
|
||||
[RealmMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a realm moderator to update the chat.");
|
||||
}
|
||||
else if (!await crs.IsMemberWithRole(chatRoom.Id, Guid.Parse(currentUser.Id), ChatMemberRole.Moderator))
|
||||
@@ -309,13 +309,9 @@ public class ChatRoomController(
|
||||
|
||||
if (request.RealmId is not null)
|
||||
{
|
||||
var member = await db.RealmMembers
|
||||
.Where(m => m.AccountId == Guid.Parse(currentUser.Id))
|
||||
.Where(m => m.RealmId == request.RealmId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (member is null || member.Role < RealmMemberRole.Moderator)
|
||||
if (!await rs.IsMemberWithRole(request.RealmId.Value, Guid.Parse(currentUser.Id), [RealmMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a moderator to transfer the chat linked to the realm.");
|
||||
chatRoom.RealmId = member.RealmId;
|
||||
chatRoom.RealmId = request.RealmId;
|
||||
}
|
||||
|
||||
if (request.PictureId is not null)
|
||||
@@ -415,7 +411,7 @@ public class ChatRoomController(
|
||||
if (chatRoom.RealmId is not null)
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(chatRoom.RealmId.Value, Guid.Parse(currentUser.Id),
|
||||
RealmMemberRole.Moderator))
|
||||
[RealmMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a realm moderator to delete the chat.");
|
||||
}
|
||||
else if (!await crs.IsMemberWithRole(chatRoom.Id, Guid.Parse(currentUser.Id), ChatMemberRole.Owner))
|
||||
@@ -507,7 +503,7 @@ public class ChatRoomController(
|
||||
.Select(m => m.AccountId)
|
||||
.ToListAsync();
|
||||
|
||||
var memberStatuses = await accountsHelper.GetAccountStatusBatch(members);
|
||||
var memberStatuses = await remoteAccountsHelper.GetAccountStatusBatch(members);
|
||||
|
||||
var onlineCount = memberStatuses.Count(s => s.Value.IsOnline);
|
||||
|
||||
@@ -546,7 +542,7 @@ public class ChatRoomController(
|
||||
.OrderBy(m => m.JoinedAt)
|
||||
.ToListAsync();
|
||||
|
||||
var memberStatuses = await accountsHelper.GetAccountStatusBatch(
|
||||
var memberStatuses = await remoteAccountsHelper.GetAccountStatusBatch(
|
||||
members.Select(m => m.AccountId).ToList()
|
||||
);
|
||||
|
||||
@@ -623,11 +619,7 @@ public class ChatRoomController(
|
||||
// Handle realm-owned chat rooms
|
||||
if (chatRoom.RealmId is not null)
|
||||
{
|
||||
var realmMember = await db.RealmMembers
|
||||
.Where(m => m.AccountId == accountId)
|
||||
.Where(m => m.RealmId == chatRoom.RealmId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (realmMember is null || realmMember.Role < RealmMemberRole.Moderator)
|
||||
if (!await rs.IsMemberWithRole(chatRoom.RealmId.Value, accountId, [RealmMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a realm moderator to invite members to this chat.");
|
||||
}
|
||||
else
|
||||
@@ -832,11 +824,7 @@ public class ChatRoomController(
|
||||
// Check if the chat room is owned by a realm
|
||||
if (chatRoom.RealmId is not null)
|
||||
{
|
||||
var realmMember = await db.RealmMembers
|
||||
.Where(m => m.AccountId == Guid.Parse(currentUser.Id))
|
||||
.Where(m => m.RealmId == chatRoom.RealmId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (realmMember is null || realmMember.Role < RealmMemberRole.Moderator)
|
||||
if (!await rs.IsMemberWithRole(chatRoom.RealmId.Value, Guid.Parse(currentUser.Id), [RealmMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a realm moderator to change member roles.");
|
||||
}
|
||||
else
|
||||
@@ -899,12 +887,12 @@ public class ChatRoomController(
|
||||
if (chatRoom.RealmId is not null)
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(chatRoom.RealmId.Value, Guid.Parse(currentUser.Id),
|
||||
RealmMemberRole.Moderator))
|
||||
[RealmMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a realm moderator to remove members.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!await crs.IsMemberWithRole(chatRoom.Id, Guid.Parse(currentUser.Id), ChatMemberRole.Moderator))
|
||||
if (!await crs.IsMemberWithRole(chatRoom.Id, Guid.Parse(currentUser.Id), [ChatMemberRole.Moderator]))
|
||||
return StatusCode(403, "You need at least be a moderator to remove members.");
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,7 @@ namespace DysonNetwork.Sphere.Chat;
|
||||
public class ChatRoomService(
|
||||
AppDatabase db,
|
||||
ICacheService cache,
|
||||
AccountClientHelper accountsHelper
|
||||
RemoteAccountService remoteAccountsHelper
|
||||
)
|
||||
{
|
||||
private const string ChatRoomGroupPrefix = "chatroom:";
|
||||
@@ -147,7 +147,7 @@ public class ChatRoomService(
|
||||
|
||||
public async Task<SnChatMember> LoadMemberAccount(SnChatMember member)
|
||||
{
|
||||
var account = await accountsHelper.GetAccount(member.AccountId);
|
||||
var account = await remoteAccountsHelper.GetAccount(member.AccountId);
|
||||
member.Account = SnAccount.FromProtoValue(account);
|
||||
return member;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ public class ChatRoomService(
|
||||
public async Task<List<SnChatMember>> LoadMemberAccounts(ICollection<SnChatMember> members)
|
||||
{
|
||||
var accountIds = members.Select(m => m.AccountId).ToList();
|
||||
var accounts = (await accountsHelper.GetAccountBatch(accountIds)).ToDictionary(a => Guid.Parse(a.Id), a => a);
|
||||
var accounts = (await remoteAccountsHelper.GetAccountBatch(accountIds)).ToDictionary(a => Guid.Parse(a.Id), a => a);
|
||||
|
||||
return
|
||||
[
|
||||
|
@@ -1,30 +1,27 @@
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DysonNetwork.Sphere.Realm;
|
||||
namespace DysonNetwork.Sphere.Chat;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/realms/{slug}")]
|
||||
public class RealmChatController(AppDatabase db, RealmService rs) : ControllerBase
|
||||
public class RealmChatController(AppDatabase db, RemoteRealmService rs) : ControllerBase
|
||||
{
|
||||
[HttpGet("chat")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<SnChatRoom>>> ListRealmChat(string slug)
|
||||
{
|
||||
var currentUser = HttpContext.Items["CurrentUser"] as Account;
|
||||
var currentUser = HttpContext.Items["CurrentUser"] as Shared.Proto.Account;
|
||||
var accountId = currentUser is null ? Guid.Empty : Guid.Parse(currentUser.Id);
|
||||
|
||||
var realm = await db.Realms
|
||||
.Where(r => r.Slug == slug)
|
||||
.FirstOrDefaultAsync();
|
||||
if (realm is null) return NotFound();
|
||||
var realm = await rs.GetRealmBySlug(slug);
|
||||
if (!realm.IsPublic)
|
||||
{
|
||||
if (currentUser is null) return Unauthorized();
|
||||
if (!await rs.IsMemberWithRole(realm.Id, accountId, RealmMemberRole.Normal))
|
||||
if (!await rs.IsMemberWithRole(realm.Id, accountId, [RealmMemberRole.Normal]))
|
||||
return StatusCode(403, "You need at least one member to view the realm's chat.");
|
||||
}
|
||||
|
2130
DysonNetwork.Sphere/Migrations/20251021154500_ChangeRealmReferenceMode.Designer.cs
generated
Normal file
2130
DysonNetwork.Sphere/Migrations/20251021154500_ChangeRealmReferenceMode.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DysonNetwork.Sphere.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ChangeRealmReferenceMode : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_chat_rooms_realms_realm_id",
|
||||
table: "chat_rooms");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_posts_realms_realm_id",
|
||||
table: "posts");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_posts_realm_id",
|
||||
table: "posts");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_chat_rooms_realm_id",
|
||||
table: "chat_rooms");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "sn_realm_id",
|
||||
table: "chat_rooms",
|
||||
type: "uuid",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_chat_rooms_sn_realm_id",
|
||||
table: "chat_rooms",
|
||||
column: "sn_realm_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_chat_rooms_realms_sn_realm_id",
|
||||
table: "chat_rooms",
|
||||
column: "sn_realm_id",
|
||||
principalTable: "realms",
|
||||
principalColumn: "id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_chat_rooms_realms_sn_realm_id",
|
||||
table: "chat_rooms");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_chat_rooms_sn_realm_id",
|
||||
table: "chat_rooms");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "sn_realm_id",
|
||||
table: "chat_rooms");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_posts_realm_id",
|
||||
table: "posts",
|
||||
column: "realm_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_chat_rooms_realm_id",
|
||||
table: "chat_rooms",
|
||||
column: "realm_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_chat_rooms_realms_realm_id",
|
||||
table: "chat_rooms",
|
||||
column: "realm_id",
|
||||
principalTable: "realms",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_posts_realms_realm_id",
|
||||
table: "posts",
|
||||
column: "realm_id",
|
||||
principalTable: "realms",
|
||||
principalColumn: "id");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ public class PollController(
|
||||
AppDatabase db,
|
||||
PollService polls,
|
||||
Publisher.PublisherService pub,
|
||||
AccountClientHelper accountsHelper
|
||||
RemoteAccountService remoteAccountsHelper
|
||||
) : ControllerBase
|
||||
{
|
||||
[HttpGet("{id:guid}")]
|
||||
@@ -110,7 +110,7 @@ public class PollController(
|
||||
if (!poll.IsAnonymous)
|
||||
{
|
||||
var answeredAccountsId = answers.Select(x => x.AccountId).Distinct().ToList();
|
||||
var answeredAccounts = await accountsHelper.GetAccountBatch(answeredAccountsId);
|
||||
var answeredAccounts = await remoteAccountsHelper.GetAccountBatch(answeredAccountsId);
|
||||
|
||||
// Populate Account field for each answer
|
||||
foreach (var answer in answers)
|
||||
|
@@ -6,7 +6,7 @@ using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using DysonNetwork.Sphere.Poll;
|
||||
using DysonNetwork.Sphere.Realm;
|
||||
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -24,12 +24,12 @@ public class PostController(
|
||||
AppDatabase db,
|
||||
PostService ps,
|
||||
PublisherService pub,
|
||||
AccountClientHelper accountsHelper,
|
||||
RemoteAccountService remoteAccountsHelper,
|
||||
AccountService.AccountServiceClient accounts,
|
||||
ActionLogService.ActionLogServiceClient als,
|
||||
PaymentService.PaymentServiceClient payments,
|
||||
PollService polls,
|
||||
RealmService rs
|
||||
RemoteRealmService rs
|
||||
)
|
||||
: ControllerBase
|
||||
{
|
||||
@@ -108,7 +108,7 @@ public class PostController(
|
||||
var userRealms = currentUser is null ? [] : await rs.GetUserRealms(accountId);
|
||||
|
||||
var publisher = pubName == null ? null : await db.Publishers.FirstOrDefaultAsync(p => p.Name == pubName);
|
||||
var realm = realmName == null ? null : await db.Realms.FirstOrDefaultAsync(r => r.Slug == realmName);
|
||||
var realm = realmName == null ? null : (realmName != null ? await rs.GetRealmBySlug(realmName) : null);
|
||||
|
||||
var query = db.Posts
|
||||
.Include(e => e.Categories)
|
||||
@@ -274,7 +274,7 @@ public class PostController(
|
||||
.Skip(offset)
|
||||
.ToListAsync();
|
||||
|
||||
var accountsProto = await accountsHelper.GetAccountBatch(reactions.Select(r => r.AccountId).ToList());
|
||||
var accountsProto = await remoteAccountsHelper.GetAccountBatch(reactions.Select(r => r.AccountId).ToList());
|
||||
var accounts = accountsProto.ToDictionary(a => Guid.Parse(a.Id), a => SnAccount.FromProtoValue(a));
|
||||
|
||||
foreach (var reaction in reactions)
|
||||
@@ -480,9 +480,8 @@ public class PostController(
|
||||
|
||||
if (request.RealmId is not null)
|
||||
{
|
||||
var realm = await db.Realms.FindAsync(request.RealmId.Value);
|
||||
if (realm is null) return BadRequest("Realm was not found.");
|
||||
if (!await rs.IsMemberWithRole(realm.Id, accountId, RealmMemberRole.Normal))
|
||||
var realm = await rs.GetRealm(request.RealmId.Value.ToString());
|
||||
if (!await rs.IsMemberWithRole(realm.Id, accountId, new List<int> { RealmMemberRole.Normal }))
|
||||
return StatusCode(403, "You are not a member of this realm.");
|
||||
post.RealmId = realm.Id;
|
||||
}
|
||||
@@ -708,7 +707,7 @@ public class PostController(
|
||||
|
||||
if (request.Mode == PostPinMode.RealmPage && post.RealmId != null)
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(post.RealmId.Value, accountId, RealmMemberRole.Moderator))
|
||||
if (!await rs.IsMemberWithRole(post.RealmId.Value, accountId, new List<int> { RealmMemberRole.Moderator }))
|
||||
return StatusCode(403, "You are not a moderator of this realm");
|
||||
}
|
||||
|
||||
@@ -756,7 +755,7 @@ public class PostController(
|
||||
|
||||
if (post is { PinMode: PostPinMode.RealmPage, RealmId: not null })
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(post.RealmId.Value, accountId, RealmMemberRole.Moderator))
|
||||
if (!await rs.IsMemberWithRole(post.RealmId.Value, accountId, new List<int> { RealmMemberRole.Moderator }))
|
||||
return StatusCode(403, "You are not a moderator of this realm");
|
||||
}
|
||||
|
||||
@@ -865,9 +864,8 @@ public class PostController(
|
||||
// The realm is the same as well as the poll
|
||||
if (request.RealmId is not null)
|
||||
{
|
||||
var realm = await db.Realms.FindAsync(request.RealmId.Value);
|
||||
if (realm is null) return BadRequest("Realm was not found.");
|
||||
if (!await rs.IsMemberWithRole(realm.Id, accountId, RealmMemberRole.Normal))
|
||||
var realm = await rs.GetRealm(request.RealmId.Value.ToString());
|
||||
if (!await rs.IsMemberWithRole(realm.Id, accountId, new List<int> { RealmMemberRole.Normal }))
|
||||
return StatusCode(403, "You are not a member of this realm.");
|
||||
post.RealmId = realm.Id;
|
||||
}
|
||||
|
@@ -938,7 +938,7 @@ public partial class PostService(
|
||||
var pub = scope.ServiceProvider.GetRequiredService<Publisher.PublisherService>();
|
||||
var nty = scope.ServiceProvider.GetRequiredService<RingService.RingServiceClient>();
|
||||
var accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
||||
var accountsHelper = scope.ServiceProvider.GetRequiredService<AccountClientHelper>();
|
||||
var accountsHelper = scope.ServiceProvider.GetRequiredService<RemoteAccountService>();
|
||||
try
|
||||
{
|
||||
var sender = await accountsHelper.GetAccount(accountId);
|
||||
|
@@ -11,7 +11,7 @@ public class PublisherService(
|
||||
AppDatabase db,
|
||||
FileReferenceService.FileReferenceServiceClient fileRefs,
|
||||
ICacheService cache,
|
||||
AccountClientHelper accountsHelper
|
||||
RemoteAccountService remoteAccountsHelper
|
||||
)
|
||||
{
|
||||
public async Task<SnPublisher?> GetPublisherByName(string name)
|
||||
@@ -420,7 +420,7 @@ public class PublisherService(
|
||||
|
||||
public async Task<SnPublisherMember> LoadMemberAccount(SnPublisherMember member)
|
||||
{
|
||||
var account = await accountsHelper.GetAccount(member.AccountId);
|
||||
var account = await remoteAccountsHelper.GetAccount(member.AccountId);
|
||||
member.Account = SnAccount.FromProtoValue(account);
|
||||
return member;
|
||||
}
|
||||
@@ -428,7 +428,7 @@ public class PublisherService(
|
||||
public async Task<List<SnPublisherMember>> LoadMemberAccounts(ICollection<SnPublisherMember> members)
|
||||
{
|
||||
var accountIds = members.Select(m => m.AccountId).ToList();
|
||||
var accounts = (await accountsHelper.GetAccountBatch(accountIds)).ToDictionary(a => Guid.Parse(a.Id), a => a);
|
||||
var accounts = (await remoteAccountsHelper.GetAccountBatch(accountIds)).ToDictionary(a => Guid.Parse(a.Id), a => a);
|
||||
|
||||
return [.. members.Select(m =>
|
||||
{
|
||||
|
@@ -134,10 +134,6 @@ public class BroadcastEventHandler(
|
||||
.Where(m => m.AccountId == evt.AccountId)
|
||||
.ExecuteDeleteAsync(cancellationToken: stoppingToken);
|
||||
|
||||
await db.RealmMembers
|
||||
.Where(m => m.AccountId == evt.AccountId)
|
||||
.ExecuteDeleteAsync(cancellationToken: stoppingToken);
|
||||
|
||||
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken: stoppingToken);
|
||||
try
|
||||
{
|
||||
|
@@ -5,7 +5,6 @@ using DysonNetwork.Sphere.Chat.Realtime;
|
||||
using DysonNetwork.Sphere.Localization;
|
||||
using DysonNetwork.Sphere.Post;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using DysonNetwork.Sphere.Realm;
|
||||
using DysonNetwork.Sphere.Sticker;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using NodaTime;
|
||||
@@ -110,7 +109,6 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<PublisherSubscriptionService>();
|
||||
services.AddScoped<ActivityService>();
|
||||
services.AddScoped<PostService>();
|
||||
services.AddScoped<RealmService>();
|
||||
services.AddScoped<ChatRoomService>();
|
||||
services.AddScoped<ChatService>();
|
||||
services.AddScoped<StickerService>();
|
||||
@@ -119,7 +117,8 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<WebFeedService>();
|
||||
services.AddScoped<DiscoveryService>();
|
||||
services.AddScoped<PollService>();
|
||||
services.AddScoped<AccountClientHelper>();
|
||||
services.AddScoped<RemoteAccountService>();
|
||||
services.AddScoped<RemoteRealmService>();
|
||||
services.AddScoped<AutocompletionService>();
|
||||
|
||||
var translationProvider = configuration["Translation:Provider"]?.ToLower();
|
||||
|
Reference in New Issue
Block a user