✨ Chat controller
This commit is contained in:
parent
8b5ca265b8
commit
da6a891b5f
@ -176,7 +176,7 @@ public class AppDatabase(
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
modelBuilder.Entity<Chat.ChatMember>()
|
modelBuilder.Entity<Chat.ChatMember>()
|
||||||
.HasKey(pm => new { pm.ChatRoom, pm.AccountId });
|
.HasKey(pm => new { pm.ChatRoomId, pm.AccountId });
|
||||||
modelBuilder.Entity<Chat.ChatMember>()
|
modelBuilder.Entity<Chat.ChatMember>()
|
||||||
.HasOne(pm => pm.ChatRoom)
|
.HasOne(pm => pm.ChatRoom)
|
||||||
.WithMany(p => p.Members)
|
.WithMany(p => p.Members)
|
||||||
|
@ -1,50 +1,99 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using DysonNetwork.Sphere.Account;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using DysonNetwork.Sphere.Account;
|
using DysonNetwork.Sphere.Realm;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
using DysonNetwork.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Chat;
|
namespace DysonNetwork.Sphere.Chat;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/chatrooms")]
|
[Route("/chat")]
|
||||||
public class ChatRoomController(AppDatabase db, FileService fs) : ControllerBase
|
public class ChatRoomController(AppDatabase db, FileService fs) : ControllerBase
|
||||||
{
|
{
|
||||||
|
[HttpGet("{id:long}")]
|
||||||
|
public async Task<ActionResult<ChatRoom>> GetChatRoom(long id)
|
||||||
|
{
|
||||||
|
var chatRoom = await db.ChatRooms
|
||||||
|
.Where(c => c.Id == id)
|
||||||
|
.Include(e => e.Picture)
|
||||||
|
.Include(e => e.Background)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (chatRoom is null) return NotFound();
|
||||||
|
return Ok(chatRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<List<ChatRoom>>> ListJoinedChatRooms()
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
var userId = currentUser.Id;
|
||||||
|
|
||||||
|
var members = await db.ChatMembers
|
||||||
|
.Where(m => m.AccountId == userId)
|
||||||
|
.Where(m => m.JoinedAt != null)
|
||||||
|
.Include(e => e.ChatRoom)
|
||||||
|
.Include(e => e.ChatRoom.Picture)
|
||||||
|
.Include(e => e.ChatRoom.Background)
|
||||||
|
.Include(e => e.ChatRoom.Type == ChatRoomType.DirectMessage ? e.ChatRoom.Members : null)
|
||||||
|
.Select(m => m.ChatRoom)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return members.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChatRoomRequest
|
||||||
|
{
|
||||||
|
[Required] [MaxLength(1024)] public string? Name { get; set; }
|
||||||
|
[MaxLength(4096)] public string? Description { get; set; }
|
||||||
|
public string? PictureId { get; set; }
|
||||||
|
public string? BackgroundId { get; set; }
|
||||||
|
public long? RealmId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult<ChatRoom>> CreateChatRoom(ChatRoomRequest request)
|
public async Task<ActionResult<ChatRoom>> CreateChatRoom(ChatRoomRequest request)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
if (request.Name is null) return BadRequest("You cannot create a chat room without a name.");
|
if (request.Name is null) return BadRequest("You cannot create a chat room without a name.");
|
||||||
|
|
||||||
CloudFile? picture = null;
|
|
||||||
if (request.PictureId is not null)
|
|
||||||
{
|
|
||||||
picture = await db.Files.FindAsync(request.PictureId);
|
|
||||||
if (picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
|
||||||
}
|
|
||||||
|
|
||||||
CloudFile? background = null;
|
|
||||||
if (request.BackgroundId is not null)
|
|
||||||
{
|
|
||||||
background = await db.Files.FindAsync(request.BackgroundId);
|
|
||||||
if (background is null) return BadRequest("Invalid background id, unable to find the file on cloud.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var chatRoom = new ChatRoom
|
var chatRoom = new ChatRoom
|
||||||
{
|
{
|
||||||
Name = request.Name,
|
Name = request.Name,
|
||||||
Description = request.Description ?? string.Empty,
|
Description = request.Description ?? string.Empty,
|
||||||
Picture = picture,
|
Members = new List<ChatMember>
|
||||||
Background = background,
|
{
|
||||||
CreatedAt = Instant.FromDateTimeUtc(DateTime.UtcNow)
|
new()
|
||||||
|
{
|
||||||
|
Role = ChatMemberRole.Owner,
|
||||||
|
AccountId = currentUser.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (request.RealmId is not null)
|
||||||
|
{
|
||||||
|
var member = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id)
|
||||||
|
.Where(m => m.RealmId == request.RealmId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (member is null || member.Role < RealmMemberRole.Moderator)
|
||||||
|
return StatusCode(403, "You need at least be a moderator to create chat linked to the realm.");
|
||||||
|
chatRoom.RealmId = member.RealmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.PictureId is not null)
|
||||||
|
{
|
||||||
|
chatRoom.Picture = await db.Files.FindAsync(request.PictureId);
|
||||||
|
if (chatRoom.Picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.BackgroundId is not null)
|
||||||
|
{
|
||||||
|
chatRoom.Background = await db.Files.FindAsync(request.BackgroundId);
|
||||||
|
if (chatRoom.Background is null)
|
||||||
|
return BadRequest("Invalid background id, unable to find the file on cloud.");
|
||||||
|
}
|
||||||
|
|
||||||
db.ChatRooms.Add(chatRoom);
|
db.ChatRooms.Add(chatRoom);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
@ -56,79 +105,116 @@ public class ChatRoomController(AppDatabase db, FileService fs) : ControllerBase
|
|||||||
return Ok(chatRoom);
|
return Ok(chatRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpPut("{id:long}")]
|
||||||
public async Task<ActionResult<ChatRoom>> GetChatRoom(long id)
|
|
||||||
{
|
|
||||||
var chatRoom = await db.ChatRooms.FindAsync(id);
|
|
||||||
if (chatRoom is null) return NotFound();
|
|
||||||
return Ok(chatRoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<ActionResult<List<ChatRoom>>> ListChatRooms()
|
|
||||||
{
|
|
||||||
var chatRooms = await db.ChatRooms.ToListAsync();
|
|
||||||
return chatRooms;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChatRoomRequest
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
[MaxLength(1024)]
|
|
||||||
public string? Name { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(4096)] public string? Description { get; set; }
|
|
||||||
public string? PictureId { get; set; }
|
|
||||||
public string? BackgroundId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
|
||||||
public async Task<ActionResult<ChatRoom>> UpdateChatRoom(long id, [FromBody] ChatRoomRequest request)
|
public async Task<ActionResult<ChatRoom>> UpdateChatRoom(long id, [FromBody] ChatRoomRequest request)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
var chatRoom = await db.ChatRooms.FindAsync(id);
|
var chatRoom = await db.ChatRooms
|
||||||
|
.Where(e => e.Id == id)
|
||||||
|
.Include(c => c.Picture)
|
||||||
|
.Include(c => c.Background)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
if (chatRoom is null) return NotFound();
|
if (chatRoom is null) return NotFound();
|
||||||
|
|
||||||
CloudFile? picture = null;
|
if (chatRoom.RealmId is not null)
|
||||||
|
{
|
||||||
|
var realmMember = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id)
|
||||||
|
.Where(m => m.RealmId == chatRoom.RealmId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (realmMember is null || realmMember.Role < RealmMemberRole.Moderator)
|
||||||
|
return StatusCode(403, "You need at least be a realm moderator to update the chat.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var chatMember = await db.ChatMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id)
|
||||||
|
.Where(m => m.ChatRoomId == chatRoom.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (chatMember is null || chatMember.Role < ChatMemberRole.Moderator)
|
||||||
|
return StatusCode(403, "You need at least be a moderator to update the chat.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.RealmId is not null)
|
||||||
|
{
|
||||||
|
var member = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id)
|
||||||
|
.Where(m => m.RealmId == request.RealmId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (member is null || member.Role < RealmMemberRole.Moderator)
|
||||||
|
return StatusCode(403, "You need at least be a moderator to transfer the chat linked to the realm.");
|
||||||
|
chatRoom.RealmId = member.RealmId;
|
||||||
|
}
|
||||||
|
|
||||||
if (request.PictureId is not null)
|
if (request.PictureId is not null)
|
||||||
{
|
{
|
||||||
picture = await db.Files.FindAsync(request.PictureId);
|
var picture = await db.Files.FindAsync(request.PictureId);
|
||||||
if (picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
if (picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
||||||
|
await fs.MarkUsageAsync(picture, 1);
|
||||||
|
if (chatRoom.Picture is not null) await fs.MarkUsageAsync(chatRoom.Picture, -1);
|
||||||
chatRoom.Picture = picture;
|
chatRoom.Picture = picture;
|
||||||
}
|
}
|
||||||
else if(request.PictureId == "")
|
|
||||||
{
|
|
||||||
chatRoom.Picture = null;
|
|
||||||
}
|
|
||||||
CloudFile? background = null;
|
|
||||||
if (request.BackgroundId is not null)
|
if (request.BackgroundId is not null)
|
||||||
{
|
{
|
||||||
background = await db.Files.FindAsync(request.BackgroundId);
|
var background = await db.Files.FindAsync(request.BackgroundId);
|
||||||
if (background is null) return BadRequest("Invalid background id, unable to find the file on cloud.");
|
if (background is null) return BadRequest("Invalid background id, unable to find the file on cloud.");
|
||||||
|
await fs.MarkUsageAsync(background, 1);
|
||||||
|
if (chatRoom.Background is not null) await fs.MarkUsageAsync(chatRoom.Background, -1);
|
||||||
|
chatRoom.Background = background;
|
||||||
}
|
}
|
||||||
else if(request.BackgroundId == "")
|
|
||||||
{
|
|
||||||
chatRoom.Background = null;
|
|
||||||
}
|
|
||||||
if (request.Name is not null)
|
if (request.Name is not null)
|
||||||
chatRoom.Name = request.Name;
|
chatRoom.Name = request.Name;
|
||||||
|
if (request.Description is not null)
|
||||||
|
chatRoom.Description = request.Description;
|
||||||
|
|
||||||
db.ChatRooms.Update(chatRoom);
|
db.ChatRooms.Update(chatRoom);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
return Ok(chatRoom);
|
return Ok(chatRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id:long}")]
|
||||||
public async Task<ActionResult> DeleteChatRoom(long id)
|
public async Task<ActionResult> DeleteChatRoom(long id)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
var chatRoom = await db.ChatRooms.FindAsync(id);
|
var chatRoom = await db.ChatRooms
|
||||||
|
.Where(e => e.Id == id)
|
||||||
|
.Include(c => c.Picture)
|
||||||
|
.Include(c => c.Background)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
if (chatRoom is null) return NotFound();
|
if (chatRoom is null) return NotFound();
|
||||||
|
|
||||||
|
if (chatRoom.RealmId is not null)
|
||||||
|
{
|
||||||
|
var realmMember = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id)
|
||||||
|
.Where(m => m.RealmId == chatRoom.RealmId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (realmMember is null || realmMember.Role < RealmMemberRole.Moderator)
|
||||||
|
return StatusCode(403, "You need at least be a realm moderator to delete the chat.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var chatMember = await db.ChatMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id)
|
||||||
|
.Where(m => m.ChatRoomId == chatRoom.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (chatMember is null || chatMember.Role < ChatMemberRole.Owner)
|
||||||
|
return StatusCode(403, "You need at least be the owner to delete the chat.");
|
||||||
|
}
|
||||||
|
|
||||||
db.ChatRooms.Remove(chatRoom);
|
db.ChatRooms.Remove(chatRoom);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
if (chatRoom.Picture is not null)
|
||||||
|
await fs.MarkUsageAsync(chatRoom.Picture, -1);
|
||||||
|
if (chatRoom.Background is not null)
|
||||||
|
await fs.MarkUsageAsync(chatRoom.Background, -1);
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
2085
DysonNetwork.Sphere/Migrations/20250502040651_RealmAndChat.Designer.cs
generated
Normal file
2085
DysonNetwork.Sphere/Migrations/20250502040651_RealmAndChat.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class RealmAndChat : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Instant>(
|
||||||
|
name: "created_at",
|
||||||
|
table: "publisher_members",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: NodaTime.Instant.FromUnixTimeTicks(0L));
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<Instant>(
|
||||||
|
name: "deleted_at",
|
||||||
|
table: "publisher_members",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<Instant>(
|
||||||
|
name: "updated_at",
|
||||||
|
table: "publisher_members",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: NodaTime.Instant.FromUnixTimeTicks(0L));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "created_at",
|
||||||
|
table: "publisher_members");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "deleted_at",
|
||||||
|
table: "publisher_members");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "updated_at",
|
||||||
|
table: "publisher_members");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -645,6 +645,117 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.ToTable("auth_sessions", (string)null);
|
b.ToTable("auth_sessions", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatMember", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("ChatRoomId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("chat_room_id");
|
||||||
|
|
||||||
|
b.Property<long>("AccountId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.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<bool>("IsBot")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_bot");
|
||||||
|
|
||||||
|
b.Property<Instant?>("JoinedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("joined_at");
|
||||||
|
|
||||||
|
b.Property<int>("Role")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("role");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("ChatRoomId", "AccountId")
|
||||||
|
.HasName("pk_chat_members");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId")
|
||||||
|
.HasDatabaseName("ix_chat_members_account_id");
|
||||||
|
|
||||||
|
b.ToTable("chat_members", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("BackgroundId")
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.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>("IsPublic")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_public");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<string>("PictureId")
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("picture_id");
|
||||||
|
|
||||||
|
b.Property<long?>("RealmId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("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_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.Permission.PermissionGroup", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -1192,6 +1303,132 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.ToTable("publisher_members", (string)null);
|
b.ToTable("publisher_members", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<long>("AccountId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("account_id");
|
||||||
|
|
||||||
|
b.Property<string>("BackgroundId")
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.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<string>("PictureId")
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.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<string>("VerifiedAs")
|
||||||
|
.HasMaxLength(4096)
|
||||||
|
.HasColumnType("character varying(4096)")
|
||||||
|
.HasColumnName("verified_as");
|
||||||
|
|
||||||
|
b.Property<Instant?>("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<long>("RealmId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("realm_id");
|
||||||
|
|
||||||
|
b.Property<long>("AccountId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.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<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.HasIndex("AccountId")
|
||||||
|
.HasDatabaseName("ix_realm_members_account_id");
|
||||||
|
|
||||||
|
b.ToTable("realm_members", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
@ -1492,6 +1729,51 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.Navigation("Challenge");
|
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.Permission.PermissionGroupMember", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroupMember", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DysonNetwork.Sphere.Permission.PermissionGroup", "Group")
|
b.HasOne("DysonNetwork.Sphere.Permission.PermissionGroup", "Group")
|
||||||
@ -1627,6 +1909,53 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.Navigation("Publisher");
|
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 =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DysonNetwork.Sphere.Account.Account", "Account")
|
b.HasOne("DysonNetwork.Sphere.Account.Account", "Account")
|
||||||
@ -1713,6 +2042,11 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.Navigation("Sessions");
|
b.Navigation("Sessions");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatRoom", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Members");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Members");
|
b.Navigation("Members");
|
||||||
@ -1735,6 +2069,13 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
|
|
||||||
b.Navigation("Posts");
|
b.Navigation("Posts");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Realm.Realm", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("ChatRooms");
|
||||||
|
|
||||||
|
b.Navigation("Members");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ public enum PublisherMemberRole
|
|||||||
Viewer = 25
|
Viewer = 25
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PublisherMember
|
public class PublisherMember : ModelBase
|
||||||
{
|
{
|
||||||
public long PublisherId { get; set; }
|
public long PublisherId { get; set; }
|
||||||
[JsonIgnore] public Publisher Publisher { get; set; } = null!;
|
[JsonIgnore] public Publisher Publisher { get; set; } = null!;
|
||||||
|
38
DysonNetwork.Sphere/Realm/RealmChatController.cs
Normal file
38
DysonNetwork.Sphere/Realm/RealmChatController.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using DysonNetwork.Sphere.Chat;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Realm;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/realm/{slug}")]
|
||||||
|
public class RealmChatController(AppDatabase db) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet("/chat")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<List<ChatRoom>>> ListRealmChat(string slug)
|
||||||
|
{
|
||||||
|
var currentUser = HttpContext.Items["CurrentUser"] as Account.Account;
|
||||||
|
|
||||||
|
var realm = await db.Realms
|
||||||
|
.Where(r => r.Slug == slug)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (realm is null) return NotFound();
|
||||||
|
if (!realm.IsPublic)
|
||||||
|
{
|
||||||
|
if (currentUser is null) return Unauthorized();
|
||||||
|
var member = await db.ChatMembers
|
||||||
|
.Where(m => m.ChatRoomId == realm.Id)
|
||||||
|
.Where(m => m.AccountId == currentUser.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (member is null) return BadRequest("You need at least one member to view the realm's chat.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatRooms = await db.ChatRooms
|
||||||
|
.Where(c => c.RealmId == realm.Id)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(chatRooms);
|
||||||
|
}
|
||||||
|
}
|
@ -13,8 +13,6 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
[HttpGet("{slug}")]
|
[HttpGet("{slug}")]
|
||||||
public async Task<ActionResult<Realm>> GetRealm(string slug)
|
public async Task<ActionResult<Realm>> GetRealm(string slug)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
|
||||||
|
|
||||||
var realm = await db.Realms
|
var realm = await db.Realms
|
||||||
.Where(e => e.Slug == slug)
|
.Where(e => e.Slug == slug)
|
||||||
.Include(e => e.Picture)
|
.Include(e => e.Picture)
|
||||||
@ -27,7 +25,7 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ActionResult<List<Realm>>> ListManagedRealms()
|
public async Task<ActionResult<List<Realm>>> ListJoinedRealms()
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
var userId = currentUser.Id;
|
var userId = currentUser.Id;
|
||||||
@ -38,10 +36,13 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
.Include(e => e.Realm)
|
.Include(e => e.Realm)
|
||||||
.Include(e => e.Realm.Picture)
|
.Include(e => e.Realm.Picture)
|
||||||
.Include(e => e.Realm.Background)
|
.Include(e => e.Realm.Background)
|
||||||
|
.Select(m => m.Realm)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return members.Select(m => m.Realm).ToList();
|
return members.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("/")]
|
||||||
|
|
||||||
[HttpGet("invites")]
|
[HttpGet("invites")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
@ -179,7 +180,15 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
Description = request.Description!,
|
Description = request.Description!,
|
||||||
AccountId = currentUser.Id,
|
AccountId = currentUser.Id,
|
||||||
IsCommunity = request.IsCommunity ?? false,
|
IsCommunity = request.IsCommunity ?? false,
|
||||||
IsPublic = request.IsPublic ?? false
|
IsPublic = request.IsPublic ?? false,
|
||||||
|
Members = new List<RealmMember>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Role = RealmMemberRole.Owner,
|
||||||
|
AccountId = currentUser.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (request.PictureId is not null)
|
if (request.PictureId is not null)
|
||||||
@ -242,6 +251,8 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
{
|
{
|
||||||
var picture = await db.Files.FindAsync(request.PictureId);
|
var picture = await db.Files.FindAsync(request.PictureId);
|
||||||
if (picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
if (picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
||||||
|
await fs.MarkUsageAsync(picture, 1);
|
||||||
|
if (realm.Picture is not null) await fs.MarkUsageAsync(realm.Picture, -1);
|
||||||
realm.Picture = picture;
|
realm.Picture = picture;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,6 +260,8 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
{
|
{
|
||||||
var background = await db.Files.FindAsync(request.BackgroundId);
|
var background = await db.Files.FindAsync(request.BackgroundId);
|
||||||
if (background is null) return BadRequest("Invalid background id, unable to find the file on cloud.");
|
if (background is null) return BadRequest("Invalid background id, unable to find the file on cloud.");
|
||||||
|
await fs.MarkUsageAsync(background, 1);
|
||||||
|
if (realm.Background is not null) await fs.MarkUsageAsync(realm.Background, -1);
|
||||||
realm.Background = background;
|
realm.Background = background;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,6 +278,8 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
|
|
||||||
var realm = await db.Realms
|
var realm = await db.Realms
|
||||||
.Where(r => r.Slug == slug)
|
.Where(r => r.Slug == slug)
|
||||||
|
.Include(r => r.Picture)
|
||||||
|
.Include(r => r.Background)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (realm is null) return NotFound();
|
if (realm is null) return NotFound();
|
||||||
|
|
||||||
@ -276,6 +291,12 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) :
|
|||||||
|
|
||||||
db.Realms.Remove(realm);
|
db.Realms.Remove(realm);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
if (realm.Picture is not null)
|
||||||
|
await fs.MarkUsageAsync(realm.Picture, -1);
|
||||||
|
if (realm.Background is not null)
|
||||||
|
await fs.MarkUsageAsync(realm.Background, -1);
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user