Compare commits
No commits in common. "f6acb3f2f021bfc227dc6e958d51ee5ed0dab919" and "17de9a0f23b8fdecaca8744768fe5f35681105c9" have entirely different histories.
f6acb3f2f0
...
17de9a0f23
@ -1,6 +1,8 @@
|
|||||||
|
using Casbin;
|
||||||
using DysonNetwork.Sphere.Permission;
|
using DysonNetwork.Sphere.Permission;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Account;
|
namespace DysonNetwork.Sphere.Account;
|
||||||
|
|
||||||
|
@ -134,6 +134,7 @@ public class AppDatabase(
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
modelBuilder.Entity<Post.Post>()
|
modelBuilder.Entity<Post.Post>()
|
||||||
|
.HasGeneratedTsVectorColumn(p => p.SearchVector, "simple", p => new { p.Title, p.Description, p.Content })
|
||||||
.HasIndex(p => p.SearchVector)
|
.HasIndex(p => p.SearchVector)
|
||||||
.HasMethod("GIN");
|
.HasMethod("GIN");
|
||||||
modelBuilder.Entity<Post.Post>()
|
modelBuilder.Entity<Post.Post>()
|
||||||
@ -192,16 +193,6 @@ public class AppDatabase(
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
modelBuilder.Entity<Chat.MessageStatus>()
|
modelBuilder.Entity<Chat.MessageStatus>()
|
||||||
.HasKey(e => new { e.MessageId, e.SenderId });
|
.HasKey(e => new { e.MessageId, e.SenderId });
|
||||||
modelBuilder.Entity<Chat.Message>()
|
|
||||||
.HasOne(m => m.ForwardedMessage)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(m => m.ForwardedMessageId)
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
modelBuilder.Entity<Chat.Message>()
|
|
||||||
.HasOne(m => m.RepliedMessage)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(m => m.RepliedMessageId)
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
// Automatically apply soft-delete filter to all entities inheriting BaseModel
|
// Automatically apply soft-delete filter to all entities inheriting BaseModel
|
||||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||||
|
@ -2,6 +2,8 @@ using System.IdentityModel.Tokens.Jwt;
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Casbin;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
|
14
DysonNetwork.Sphere/Casbin.conf
Normal file
14
DysonNetwork.Sphere/Casbin.conf
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[request_definition]
|
||||||
|
r = sub, dom, obj, act
|
||||||
|
|
||||||
|
[policy_definition]
|
||||||
|
p = sub, dom, obj, act
|
||||||
|
|
||||||
|
[role_definition]
|
||||||
|
g = _, _, _
|
||||||
|
|
||||||
|
[policy_effect]
|
||||||
|
e = some(where (p.eft == allow))
|
||||||
|
|
||||||
|
[matchers]
|
||||||
|
m = regexMatch(r.sub, "^super:") || (g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act)
|
@ -1,94 +1,12 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using DysonNetwork.Sphere.Storage;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Chat;
|
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/chat")]
|
[Route("/chat")]
|
||||||
public partial class ChatController(AppDatabase db, ChatService cs) : ControllerBase
|
public class ChatController : ControllerBase
|
||||||
{
|
{
|
||||||
public class MarkMessageReadRequest
|
public class MarkMessageReadRequest
|
||||||
{
|
{
|
||||||
public Guid MessageId { get; set; }
|
public Guid MessageId { get; set; }
|
||||||
public long ChatRoomId { get; set; }
|
public long ChatRoomId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SendMessageRequest
|
|
||||||
{
|
|
||||||
[MaxLength(4096)] public string? Content { get; set; }
|
|
||||||
public List<CloudFile>? Attachments { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[GeneratedRegex(@"@([A-Za-z0-9_-]+)")]
|
|
||||||
private static partial Regex MentionRegex();
|
|
||||||
|
|
||||||
[HttpPost("{roomId:long}/messages")]
|
|
||||||
public async Task<ActionResult> SendMessage([FromBody] SendMessageRequest request, long roomId)
|
|
||||||
{
|
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
|
||||||
if (string.IsNullOrWhiteSpace(request.Content) && (request.Attachments == null || request.Attachments.Count == 0))
|
|
||||||
return BadRequest("You cannot send an empty message.");
|
|
||||||
|
|
||||||
var member = await db.ChatMembers
|
|
||||||
.Where(m => m.AccountId == currentUser.Id && m.ChatRoomId == roomId)
|
|
||||||
.Include(m => m.ChatRoom)
|
|
||||||
.Include(m => m.ChatRoom.Realm)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
if (member == null || member.Role < ChatMemberRole.Normal) return StatusCode(403, "You need to be a normal member to send messages here.");
|
|
||||||
|
|
||||||
var message = new Message
|
|
||||||
{
|
|
||||||
SenderId = member.Id,
|
|
||||||
ChatRoomId = roomId,
|
|
||||||
};
|
|
||||||
if (request.Content is not null)
|
|
||||||
message.Content = request.Content;
|
|
||||||
if (request.Attachments is not null)
|
|
||||||
message.Attachments = request.Attachments;
|
|
||||||
|
|
||||||
if (request.Content is not null)
|
|
||||||
{
|
|
||||||
var mentioned = MentionRegex()
|
|
||||||
.Matches(request.Content)
|
|
||||||
.Select(m => m.Groups[1].Value)
|
|
||||||
.ToList();
|
|
||||||
if (mentioned is not null && mentioned.Count > 0)
|
|
||||||
{
|
|
||||||
var mentionedMembers = await db.ChatMembers
|
|
||||||
.Where(m => mentioned.Contains(m.Account.Name))
|
|
||||||
.Select(m => m.Id)
|
|
||||||
.ToListAsync();
|
|
||||||
message.MembersMetioned = mentionedMembers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
member.Account = currentUser;
|
|
||||||
var result = await cs.SendMessageAsync(message, member, member.ChatRoom);
|
|
||||||
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SyncRequest
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public long LastSyncTimestamp { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{roomId:long}/sync")]
|
|
||||||
public async Task<ActionResult<SyncResponse>> GetSyncData([FromQuery] SyncRequest request, long roomId)
|
|
||||||
{
|
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
|
||||||
return Unauthorized();
|
|
||||||
|
|
||||||
var isMember = await db.ChatMembers
|
|
||||||
.AnyAsync(m => m.AccountId == currentUser.Id && m.ChatRoomId == roomId);
|
|
||||||
if (!isMember)
|
|
||||||
return StatusCode(403, "You are not a member of this chat room.");
|
|
||||||
|
|
||||||
var response = await cs.GetSyncDataAsync(roomId, request.LastSyncTimestamp);
|
|
||||||
return Ok(response);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -35,13 +35,6 @@ public enum ChatMemberRole
|
|||||||
Normal = 0
|
Normal = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ChatMemberNotify
|
|
||||||
{
|
|
||||||
All,
|
|
||||||
Mentions,
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChatMember : ModelBase
|
public class ChatMember : ModelBase
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
@ -50,10 +43,7 @@ public class ChatMember : ModelBase
|
|||||||
public long AccountId { get; set; }
|
public long AccountId { get; set; }
|
||||||
[JsonIgnore] public Account.Account Account { get; set; } = null!;
|
[JsonIgnore] public Account.Account Account { get; set; } = null!;
|
||||||
|
|
||||||
[MaxLength(1024)] public string? Nick { get; set; }
|
public ChatMemberRole Role { get; set; }
|
||||||
|
|
||||||
public ChatMemberRole Role { get; set; } = ChatMemberRole.Normal;
|
|
||||||
public ChatMemberNotify Notify { get; set; } = ChatMemberNotify.All;
|
|
||||||
public Instant? JoinedAt { get; set; }
|
public Instant? JoinedAt { get; set; }
|
||||||
public bool IsBot { get; set; } = false;
|
public bool IsBot { get; set; } = false;
|
||||||
}
|
}
|
@ -37,6 +37,7 @@ public class ChatRoomController(AppDatabase db, FileService fs) : ControllerBase
|
|||||||
.Include(e => e.ChatRoom)
|
.Include(e => e.ChatRoom)
|
||||||
.Include(e => e.ChatRoom.Picture)
|
.Include(e => e.ChatRoom.Picture)
|
||||||
.Include(e => e.ChatRoom.Background)
|
.Include(e => e.ChatRoom.Background)
|
||||||
|
.Include(e => e.ChatRoom.Type == ChatRoomType.DirectMessage ? e.ChatRoom.Members : null)
|
||||||
.Select(m => m.ChatRoom)
|
.Select(m => m.ChatRoom)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
@ -1,47 +1,9 @@
|
|||||||
using DysonNetwork.Sphere.Account;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Connection;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Chat;
|
public class ChatService(AppDatabase db)
|
||||||
|
|
||||||
public class ChatService(AppDatabase db, NotificationService nty, WebSocketService ws)
|
|
||||||
{
|
{
|
||||||
public async Task<Message> SendMessageAsync(Message message, ChatMember sender, ChatRoom room)
|
|
||||||
{
|
|
||||||
db.ChatMessages.Add(message);
|
|
||||||
await db.SaveChangesAsync();
|
|
||||||
_ = DeliverMessageAsync(message, sender, room).ConfigureAwait(false);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeliverMessageAsync(Message message, ChatMember sender, ChatRoom room)
|
|
||||||
{
|
|
||||||
var roomSubject = room.Realm is not null ? $"{room.Name}, {room.Realm.Name}" : room.Name;
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
await foreach (
|
|
||||||
var member in db.ChatMembers
|
|
||||||
.Where(m => m.ChatRoomId == message.ChatRoomId && m.AccountId != message.Sender.AccountId)
|
|
||||||
.Where(m => m.Notify != ChatMemberNotify.None)
|
|
||||||
.Where(m => m.Notify != ChatMemberNotify.Mentions || (message.MembersMetioned != null && message.MembersMetioned.Contains(m.Id)))
|
|
||||||
.AsAsyncEnumerable()
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ws.SendPacketToAccount(member.AccountId, new WebSocketPacket
|
|
||||||
{
|
|
||||||
Type = "messages.new",
|
|
||||||
Data = message
|
|
||||||
});
|
|
||||||
tasks.Add(nty.DeliveryNotification(new Notification
|
|
||||||
{
|
|
||||||
AccountId = member.AccountId,
|
|
||||||
Topic = "messages.new",
|
|
||||||
Title = $"{sender.Nick ?? sender.Account.Nick} ({roomSubject})",
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
await Task.WhenAll(tasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task MarkMessageAsReadAsync(Guid messageId, long roomId, long userId)
|
public async Task MarkMessageAsReadAsync(Guid messageId, long roomId, long userId)
|
||||||
{
|
{
|
||||||
var existingStatus = await db.ChatStatuses
|
var existingStatus = await db.ChatStatuses
|
||||||
@ -83,48 +45,4 @@ public class ChatService(AppDatabase db, NotificationService nty, WebSocketServi
|
|||||||
|
|
||||||
return messages.Count(m => !m.IsRead);
|
return messages.Count(m => !m.IsRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SyncResponse> GetSyncDataAsync(long roomId, long lastSyncTimestamp)
|
|
||||||
{
|
|
||||||
var timestamp = Instant.FromUnixTimeMilliseconds(lastSyncTimestamp);
|
|
||||||
var changes = await db.ChatMessages
|
|
||||||
.IgnoreQueryFilters()
|
|
||||||
.Where(m => m.ChatRoomId == roomId)
|
|
||||||
.Where(m => m.UpdatedAt > timestamp || m.DeletedAt > timestamp)
|
|
||||||
.Select(m => new MessageChange
|
|
||||||
{
|
|
||||||
MessageId = m.Id,
|
|
||||||
Action = m.DeletedAt != null ? "delete" : (m.EditedAt == null ? "create" : "update"),
|
|
||||||
Message = m.DeletedAt != null ? null : m,
|
|
||||||
Timestamp = m.DeletedAt != null ? m.DeletedAt.Value : m.UpdatedAt
|
|
||||||
})
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return new SyncResponse
|
|
||||||
{
|
|
||||||
Changes = changes,
|
|
||||||
CurrentTimestamp = SystemClock.Instance.GetCurrentInstant()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MessageChangeAction
|
|
||||||
{
|
|
||||||
public const string Create = "create";
|
|
||||||
public const string Update = "update";
|
|
||||||
public const string Delete = "delete";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MessageChange
|
|
||||||
{
|
|
||||||
public Guid MessageId { get; set; }
|
|
||||||
public string Action { get; set; } = null!;
|
|
||||||
public Message? Message { get; set; }
|
|
||||||
public Instant Timestamp { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SyncResponse
|
|
||||||
{
|
|
||||||
public List<MessageChange> Changes { get; set; } = [];
|
|
||||||
public Instant CurrentTimestamp { get; set; }
|
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Net.Mail;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using DysonNetwork.Sphere.Storage;
|
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Chat;
|
namespace DysonNetwork.Sphere.Chat;
|
||||||
@ -15,7 +15,7 @@ public class Message : ModelBase
|
|||||||
[Column(TypeName = "jsonb")] public List<Guid>? MembersMetioned { get; set; }
|
[Column(TypeName = "jsonb")] public List<Guid>? MembersMetioned { get; set; }
|
||||||
public Instant? EditedAt { get; set; }
|
public Instant? EditedAt { get; set; }
|
||||||
|
|
||||||
public ICollection<CloudFile> Attachments { get; set; } = new List<CloudFile>();
|
public ICollection<Attachment> Attachments { get; set; } = new List<Attachment>();
|
||||||
public ICollection<MessageReaction> Reactions { get; set; } = new List<MessageReaction>();
|
public ICollection<MessageReaction> Reactions { get; set; } = new List<MessageReaction>();
|
||||||
public ICollection<MessageStatus> Statuses { get; set; } = new List<MessageStatus>();
|
public ICollection<MessageStatus> Statuses { get; set; } = new List<MessageStatus>();
|
||||||
|
|
||||||
@ -39,7 +39,6 @@ public enum MessageReactionAttitude
|
|||||||
|
|
||||||
public class MessageReaction : ModelBase
|
public class MessageReaction : ModelBase
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
|
||||||
public Guid MessageId { get; set; }
|
public Guid MessageId { get; set; }
|
||||||
[JsonIgnore] public Message Message { get; set; } = null!;
|
[JsonIgnore] public Message Message { get; set; } = null!;
|
||||||
public Guid SenderId { get; set; }
|
public Guid SenderId { get; set; }
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
using System.Net.WebSockets;
|
|
||||||
using DysonNetwork.Sphere.Chat;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Connection.Handlers;
|
|
||||||
|
|
||||||
public class MessageReadHandler(AppDatabase db) : IWebSocketPacketHandler
|
|
||||||
{
|
|
||||||
public string PacketType => "message.read";
|
|
||||||
|
|
||||||
public async Task HandleAsync(Account.Account currentUser, string deviceId, WebSocketPacket packet, WebSocket socket)
|
|
||||||
{
|
|
||||||
var request = packet.GetData<Chat.ChatController.MarkMessageReadRequest>();
|
|
||||||
if (request is null)
|
|
||||||
{
|
|
||||||
await socket.SendAsync(
|
|
||||||
new ArraySegment<byte>(new WebSocketPacket
|
|
||||||
{
|
|
||||||
Type = WebSocketPacketType.Error,
|
|
||||||
ErrorMessage = "Mark message as read requires you provide the ChatRoomId and MessageId"
|
|
||||||
}.ToBytes()),
|
|
||||||
WebSocketMessageType.Binary,
|
|
||||||
true,
|
|
||||||
CancellationToken.None
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var existingStatus = await db.ChatStatuses
|
|
||||||
.FirstOrDefaultAsync(x => x.MessageId == request.MessageId && x.Sender.AccountId == currentUser.Id);
|
|
||||||
var sender = await db.ChatMembers
|
|
||||||
.Where(m => m.AccountId == currentUser.Id && m.ChatRoomId == request.ChatRoomId)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
if (sender is null)
|
|
||||||
{
|
|
||||||
await socket.SendAsync(
|
|
||||||
new ArraySegment<byte>(new WebSocketPacket
|
|
||||||
{
|
|
||||||
Type = WebSocketPacketType.Error,
|
|
||||||
ErrorMessage = "User is not a member of the chat room."
|
|
||||||
}.ToBytes()),
|
|
||||||
WebSocketMessageType.Binary,
|
|
||||||
true,
|
|
||||||
CancellationToken.None
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingStatus == null)
|
|
||||||
{
|
|
||||||
existingStatus = new MessageStatus
|
|
||||||
{
|
|
||||||
MessageId = request.MessageId,
|
|
||||||
SenderId = sender.Id,
|
|
||||||
};
|
|
||||||
db.ChatStatuses.Add(existingStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
using System.Net.WebSockets;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Connection;
|
|
||||||
|
|
||||||
public interface IWebSocketPacketHandler
|
|
||||||
{
|
|
||||||
string PacketType { get; }
|
|
||||||
Task HandleAsync(Account.Account currentUser, string deviceId, WebSocketPacket packet, WebSocket socket);
|
|
||||||
}
|
|
@ -1,19 +1,11 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using DysonNetwork.Sphere.Chat;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Connection;
|
namespace DysonNetwork.Sphere.Connection;
|
||||||
|
|
||||||
public class WebSocketService
|
public class WebSocketService(ChatService cs)
|
||||||
{
|
{
|
||||||
private readonly IDictionary<string, IWebSocketPacketHandler> _handlerMap;
|
public static readonly ConcurrentDictionary<
|
||||||
|
|
||||||
public WebSocketService(IEnumerable<IWebSocketPacketHandler> handlers)
|
|
||||||
{
|
|
||||||
_handlerMap = handlers.ToDictionary(h => h.PacketType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<
|
|
||||||
(long AccountId, string DeviceId),
|
(long AccountId, string DeviceId),
|
||||||
(WebSocket Socket, CancellationTokenSource Cts)
|
(WebSocket Socket, CancellationTokenSource Cts)
|
||||||
> ActiveConnections = new();
|
> ActiveConnections = new();
|
||||||
@ -25,8 +17,7 @@ public class WebSocketService
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (ActiveConnections.TryGetValue(key, out _))
|
if (ActiveConnections.TryGetValue(key, out _))
|
||||||
Disconnect(key,
|
Disconnect(key, "Just connected somewhere else with the same identifier."); // Disconnect the previous one using the same identifier
|
||||||
"Just connected somewhere else with the same identifier."); // Disconnect the previous one using the same identifier
|
|
||||||
return ActiveConnections.TryAdd(key, (socket, cts));
|
return ActiveConnections.TryAdd(key, (socket, cts));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,50 +33,30 @@ public class WebSocketService
|
|||||||
ActiveConnections.TryRemove(key, out _);
|
ActiveConnections.TryRemove(key, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendPacketToAccount(long userId, WebSocketPacket packet)
|
public void HandlePacket(Account.Account currentUser, string deviceId, WebSocketPacket packet, WebSocket socket)
|
||||||
{
|
{
|
||||||
var connections = ActiveConnections.Where(c => c.Key.AccountId == userId);
|
switch (packet.Type)
|
||||||
var packetBytes = packet.ToBytes();
|
|
||||||
var segment = new ArraySegment<byte>(packetBytes);
|
|
||||||
|
|
||||||
foreach (var connection in connections)
|
|
||||||
{
|
{
|
||||||
connection.Value.Socket.SendAsync(
|
case "message.read":
|
||||||
segment,
|
var request = packet.GetData<ChatController.MarkMessageReadRequest>();
|
||||||
|
if (request is null)
|
||||||
|
{
|
||||||
|
socket.SendAsync(
|
||||||
|
new ArraySegment<byte>(new WebSocketPacket
|
||||||
|
{
|
||||||
|
Type = WebSocketPacketType.Error,
|
||||||
|
ErrorMessage = "Mark message as read requires you provide the ChatRoomId and MessageId"
|
||||||
|
}.ToBytes()),
|
||||||
WebSocketMessageType.Binary,
|
WebSocketMessageType.Binary,
|
||||||
true,
|
true,
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
_ = cs.MarkMessageAsReadAsync(request.MessageId, currentUser.Id, currentUser.Id).ConfigureAwait(false);
|
||||||
|
break;
|
||||||
public void SendPacketToDevice(string deviceId, WebSocketPacket packet)
|
default:
|
||||||
{
|
socket.SendAsync(
|
||||||
var connections = ActiveConnections.Where(c => c.Key.DeviceId == deviceId);
|
|
||||||
var packetBytes = packet.ToBytes();
|
|
||||||
var segment = new ArraySegment<byte>(packetBytes);
|
|
||||||
|
|
||||||
foreach (var connection in connections)
|
|
||||||
{
|
|
||||||
connection.Value.Socket.SendAsync(
|
|
||||||
segment,
|
|
||||||
WebSocketMessageType.Binary,
|
|
||||||
true,
|
|
||||||
CancellationToken.None
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task HandlePacket(Account.Account currentUser, string deviceId, WebSocketPacket packet,
|
|
||||||
WebSocket socket)
|
|
||||||
{
|
|
||||||
if (_handlerMap.TryGetValue(packet.Type, out var handler))
|
|
||||||
{
|
|
||||||
await handler.HandleAsync(currentUser, deviceId, packet, socket);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await socket.SendAsync(
|
|
||||||
new ArraySegment<byte>(new WebSocketPacket
|
new ArraySegment<byte>(new WebSocketPacket
|
||||||
{
|
{
|
||||||
Type = WebSocketPacketType.Error,
|
Type = WebSocketPacketType.Error,
|
||||||
@ -95,5 +66,7 @@ public class WebSocketService
|
|||||||
true,
|
true,
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,6 +11,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
<PackageReference Include="Blurhash.ImageSharp" Version="4.0.0" />
|
<PackageReference Include="Blurhash.ImageSharp" Version="4.0.0" />
|
||||||
|
<PackageReference Include="Casbin.NET" Version="2.12.0" />
|
||||||
|
<PackageReference Include="Casbin.NET.Adapter.EFCore" Version="2.5.0" />
|
||||||
<PackageReference Include="CorePush" Version="4.3.0" />
|
<PackageReference Include="CorePush" Version="4.3.0" />
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||||
<PackageReference Include="FFMpegCore" Version="5.2.0" />
|
<PackageReference Include="FFMpegCore" Version="5.2.0" />
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,238 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddChatMessage : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropPrimaryKey(
|
|
||||||
name: "pk_chat_members",
|
|
||||||
table: "chat_members");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
|
||||||
name: "message_id",
|
|
||||||
table: "files",
|
|
||||||
type: "uuid",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
|
||||||
name: "id",
|
|
||||||
table: "chat_members",
|
|
||||||
type: "uuid",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
|
|
||||||
|
|
||||||
migrationBuilder.AddUniqueConstraint(
|
|
||||||
name: "ak_chat_members_chat_room_id_account_id",
|
|
||||||
table: "chat_members",
|
|
||||||
columns: new[] { "chat_room_id", "account_id" });
|
|
||||||
|
|
||||||
migrationBuilder.AddPrimaryKey(
|
|
||||||
name: "pk_chat_members",
|
|
||||||
table: "chat_members",
|
|
||||||
column: "id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "chat_messages",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
type = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
|
|
||||||
content = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: false),
|
|
||||||
meta = table.Column<Dictionary<string, object>>(type: "jsonb", nullable: true),
|
|
||||||
members_metioned = table.Column<List<Guid>>(type: "jsonb", nullable: true),
|
|
||||||
edited_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
|
||||||
replied_message_id = table.Column<Guid>(type: "uuid", nullable: true),
|
|
||||||
forwarded_message_id = table.Column<Guid>(type: "uuid", nullable: true),
|
|
||||||
sender_id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
chat_room_id = table.Column<long>(type: "bigint", 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_chat_messages", x => x.id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_chat_messages_chat_members_sender_id",
|
|
||||||
column: x => x.sender_id,
|
|
||||||
principalTable: "chat_members",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_chat_messages_chat_messages_forwarded_message_id",
|
|
||||||
column: x => x.forwarded_message_id,
|
|
||||||
principalTable: "chat_messages",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Restrict);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_chat_messages_chat_messages_replied_message_id",
|
|
||||||
column: x => x.replied_message_id,
|
|
||||||
principalTable: "chat_messages",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Restrict);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_chat_messages_chat_rooms_chat_room_id",
|
|
||||||
column: x => x.chat_room_id,
|
|
||||||
principalTable: "chat_rooms",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "chat_statuses",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
message_id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
sender_id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
read_at = table.Column<Instant>(type: "timestamp with time zone", 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_chat_statuses", x => new { x.message_id, x.sender_id });
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_chat_statuses_chat_members_sender_id",
|
|
||||||
column: x => x.sender_id,
|
|
||||||
principalTable: "chat_members",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_chat_statuses_chat_messages_message_id",
|
|
||||||
column: x => x.message_id,
|
|
||||||
principalTable: "chat_messages",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "message_reaction",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
message_id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
sender_id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
symbol = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
attitude = table.Column<int>(type: "integer", 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_message_reaction", x => x.id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_message_reaction_chat_members_sender_id",
|
|
||||||
column: x => x.sender_id,
|
|
||||||
principalTable: "chat_members",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_message_reaction_chat_messages_message_id",
|
|
||||||
column: x => x.message_id,
|
|
||||||
principalTable: "chat_messages",
|
|
||||||
principalColumn: "id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_files_message_id",
|
|
||||||
table: "files",
|
|
||||||
column: "message_id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_chat_messages_chat_room_id",
|
|
||||||
table: "chat_messages",
|
|
||||||
column: "chat_room_id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_chat_messages_forwarded_message_id",
|
|
||||||
table: "chat_messages",
|
|
||||||
column: "forwarded_message_id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_chat_messages_replied_message_id",
|
|
||||||
table: "chat_messages",
|
|
||||||
column: "replied_message_id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_chat_messages_sender_id",
|
|
||||||
table: "chat_messages",
|
|
||||||
column: "sender_id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_chat_statuses_sender_id",
|
|
||||||
table: "chat_statuses",
|
|
||||||
column: "sender_id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_message_reaction_message_id",
|
|
||||||
table: "message_reaction",
|
|
||||||
column: "message_id");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_message_reaction_sender_id",
|
|
||||||
table: "message_reaction",
|
|
||||||
column: "sender_id");
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "fk_files_chat_messages_message_id",
|
|
||||||
table: "files",
|
|
||||||
column: "message_id",
|
|
||||||
principalTable: "chat_messages",
|
|
||||||
principalColumn: "id");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "fk_files_chat_messages_message_id",
|
|
||||||
table: "files");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "chat_statuses");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "message_reaction");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "chat_messages");
|
|
||||||
|
|
||||||
migrationBuilder.DropIndex(
|
|
||||||
name: "ix_files_message_id",
|
|
||||||
table: "files");
|
|
||||||
|
|
||||||
migrationBuilder.DropUniqueConstraint(
|
|
||||||
name: "ak_chat_members_chat_room_id_account_id",
|
|
||||||
table: "chat_members");
|
|
||||||
|
|
||||||
migrationBuilder.DropPrimaryKey(
|
|
||||||
name: "pk_chat_members",
|
|
||||||
table: "chat_members");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "message_id",
|
|
||||||
table: "files");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "id",
|
|
||||||
table: "chat_members");
|
|
||||||
|
|
||||||
migrationBuilder.AddPrimaryKey(
|
|
||||||
name: "pk_chat_members",
|
|
||||||
table: "chat_members",
|
|
||||||
columns: new[] { "chat_room_id", "account_id" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,62 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using NpgsqlTypes;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class DontKnowHowToNameThing : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AlterColumn<NpgsqlTsVector>(
|
|
||||||
name: "search_vector",
|
|
||||||
table: "posts",
|
|
||||||
type: "tsvector",
|
|
||||||
nullable: true,
|
|
||||||
oldClrType: typeof(NpgsqlTsVector),
|
|
||||||
oldType: "tsvector")
|
|
||||||
.OldAnnotation("Npgsql:TsVectorConfig", "simple")
|
|
||||||
.OldAnnotation("Npgsql:TsVectorProperties", new[] { "title", "description", "content" });
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "nick",
|
|
||||||
table: "chat_members",
|
|
||||||
type: "character varying(1024)",
|
|
||||||
maxLength: 1024,
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
|
||||||
name: "notify",
|
|
||||||
table: "chat_members",
|
|
||||||
type: "integer",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "nick",
|
|
||||||
table: "chat_members");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "notify",
|
|
||||||
table: "chat_members");
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<NpgsqlTsVector>(
|
|
||||||
name: "search_vector",
|
|
||||||
table: "posts",
|
|
||||||
type: "tsvector",
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(NpgsqlTsVector),
|
|
||||||
oldType: "tsvector",
|
|
||||||
oldNullable: true)
|
|
||||||
.Annotation("Npgsql:TsVectorConfig", "simple")
|
|
||||||
.Annotation("Npgsql:TsVectorProperties", new[] { "title", "description", "content" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -647,19 +647,14 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatMember", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Chat.ChatMember", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<long>("ChatRoomId")
|
||||||
.ValueGeneratedOnAdd()
|
.HasColumnType("bigint")
|
||||||
.HasColumnType("uuid")
|
.HasColumnName("chat_room_id");
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<long>("AccountId")
|
b.Property<long>("AccountId")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("account_id");
|
.HasColumnName("account_id");
|
||||||
|
|
||||||
b.Property<long>("ChatRoomId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("chat_room_id");
|
|
||||||
|
|
||||||
b.Property<Instant>("CreatedAt")
|
b.Property<Instant>("CreatedAt")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("created_at");
|
.HasColumnName("created_at");
|
||||||
@ -676,15 +671,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("joined_at");
|
.HasColumnName("joined_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")
|
b.Property<int>("Role")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("role");
|
.HasColumnName("role");
|
||||||
@ -693,12 +679,9 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("updated_at");
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("ChatRoomId", "AccountId")
|
||||||
.HasName("pk_chat_members");
|
.HasName("pk_chat_members");
|
||||||
|
|
||||||
b.HasAlternateKey("ChatRoomId", "AccountId")
|
|
||||||
.HasName("ak_chat_members_chat_room_id_account_id");
|
|
||||||
|
|
||||||
b.HasIndex("AccountId")
|
b.HasIndex("AccountId")
|
||||||
.HasDatabaseName("ix_chat_members_account_id");
|
.HasDatabaseName("ix_chat_members_account_id");
|
||||||
|
|
||||||
@ -773,167 +756,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.ToTable("chat_rooms", (string)null);
|
b.ToTable("chat_rooms", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<long>("ChatRoomId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("chat_room_id");
|
|
||||||
|
|
||||||
b.Property<string>("Content")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("character varying(4096)")
|
|
||||||
.HasColumnName("content");
|
|
||||||
|
|
||||||
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?>("EditedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("edited_at");
|
|
||||||
|
|
||||||
b.Property<Guid?>("ForwardedMessageId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("forwarded_message_id");
|
|
||||||
|
|
||||||
b.Property<List<Guid>>("MembersMetioned")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("members_metioned");
|
|
||||||
|
|
||||||
b.Property<Dictionary<string, object>>("Meta")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("meta");
|
|
||||||
|
|
||||||
b.Property<Guid?>("RepliedMessageId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("replied_message_id");
|
|
||||||
|
|
||||||
b.Property<Guid>("SenderId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("sender_id");
|
|
||||||
|
|
||||||
b.Property<string>("Type")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1024)
|
|
||||||
.HasColumnType("character varying(1024)")
|
|
||||||
.HasColumnName("type");
|
|
||||||
|
|
||||||
b.Property<Instant>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("updated_at");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_chat_messages");
|
|
||||||
|
|
||||||
b.HasIndex("ChatRoomId")
|
|
||||||
.HasDatabaseName("ix_chat_messages_chat_room_id");
|
|
||||||
|
|
||||||
b.HasIndex("ForwardedMessageId")
|
|
||||||
.HasDatabaseName("ix_chat_messages_forwarded_message_id");
|
|
||||||
|
|
||||||
b.HasIndex("RepliedMessageId")
|
|
||||||
.HasDatabaseName("ix_chat_messages_replied_message_id");
|
|
||||||
|
|
||||||
b.HasIndex("SenderId")
|
|
||||||
.HasDatabaseName("ix_chat_messages_sender_id");
|
|
||||||
|
|
||||||
b.ToTable("chat_messages", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageReaction", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<int>("Attitude")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("attitude");
|
|
||||||
|
|
||||||
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<Guid>("MessageId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("message_id");
|
|
||||||
|
|
||||||
b.Property<Guid>("SenderId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("sender_id");
|
|
||||||
|
|
||||||
b.Property<string>("Symbol")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)")
|
|
||||||
.HasColumnName("symbol");
|
|
||||||
|
|
||||||
b.Property<Instant>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("updated_at");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_message_reaction");
|
|
||||||
|
|
||||||
b.HasIndex("MessageId")
|
|
||||||
.HasDatabaseName("ix_message_reaction_message_id");
|
|
||||||
|
|
||||||
b.HasIndex("SenderId")
|
|
||||||
.HasDatabaseName("ix_message_reaction_sender_id");
|
|
||||||
|
|
||||||
b.ToTable("message_reaction", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageStatus", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("MessageId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("message_id");
|
|
||||||
|
|
||||||
b.Property<Guid>("SenderId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("sender_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>("ReadAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("read_at");
|
|
||||||
|
|
||||||
b.Property<Instant>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("updated_at");
|
|
||||||
|
|
||||||
b.HasKey("MessageId", "SenderId")
|
|
||||||
.HasName("pk_chat_statuses");
|
|
||||||
|
|
||||||
b.HasIndex("SenderId")
|
|
||||||
.HasDatabaseName("ix_chat_statuses_sender_id");
|
|
||||||
|
|
||||||
b.ToTable("chat_statuses", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -1128,8 +950,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("replied_post_id");
|
.HasColumnName("replied_post_id");
|
||||||
|
|
||||||
b.Property<NpgsqlTsVector>("SearchVector")
|
b.Property<NpgsqlTsVector>("SearchVector")
|
||||||
|
.IsRequired()
|
||||||
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
.HasColumnType("tsvector")
|
.HasColumnType("tsvector")
|
||||||
.HasColumnName("search_vector");
|
.HasColumnName("search_vector")
|
||||||
|
.HasAnnotation("Npgsql:TsVectorConfig", "simple")
|
||||||
|
.HasAnnotation("Npgsql:TsVectorProperties", new[] { "Title", "Description", "Content" });
|
||||||
|
|
||||||
b.Property<long?>("ThreadedPostId")
|
b.Property<long?>("ThreadedPostId")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
@ -1644,10 +1470,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("character varying(256)")
|
.HasColumnType("character varying(256)")
|
||||||
.HasColumnName("hash");
|
.HasColumnName("hash");
|
||||||
|
|
||||||
b.Property<Guid?>("MessageId")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasColumnName("message_id");
|
|
||||||
|
|
||||||
b.Property<string>("MimeType")
|
b.Property<string>("MimeType")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("character varying(256)")
|
.HasColumnType("character varying(256)")
|
||||||
@ -1694,9 +1516,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasIndex("AccountId")
|
b.HasIndex("AccountId")
|
||||||
.HasDatabaseName("ix_files_account_id");
|
.HasDatabaseName("ix_files_account_id");
|
||||||
|
|
||||||
b.HasIndex("MessageId")
|
|
||||||
.HasDatabaseName("ix_files_message_id");
|
|
||||||
|
|
||||||
b.HasIndex("PostId")
|
b.HasIndex("PostId")
|
||||||
.HasDatabaseName("ix_files_post_id");
|
.HasDatabaseName("ix_files_post_id");
|
||||||
|
|
||||||
@ -1955,85 +1774,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.Navigation("Realm");
|
b.Navigation("Realm");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.ChatRoom", "ChatRoom")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("ChatRoomId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_chat_messages_chat_rooms_chat_room_id");
|
|
||||||
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.Message", "ForwardedMessage")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("ForwardedMessageId")
|
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
|
||||||
.HasConstraintName("fk_chat_messages_chat_messages_forwarded_message_id");
|
|
||||||
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.Message", "RepliedMessage")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("RepliedMessageId")
|
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
|
||||||
.HasConstraintName("fk_chat_messages_chat_messages_replied_message_id");
|
|
||||||
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("SenderId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_chat_messages_chat_members_sender_id");
|
|
||||||
|
|
||||||
b.Navigation("ChatRoom");
|
|
||||||
|
|
||||||
b.Navigation("ForwardedMessage");
|
|
||||||
|
|
||||||
b.Navigation("RepliedMessage");
|
|
||||||
|
|
||||||
b.Navigation("Sender");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageReaction", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.Message", "Message")
|
|
||||||
.WithMany("Reactions")
|
|
||||||
.HasForeignKey("MessageId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_message_reaction_chat_messages_message_id");
|
|
||||||
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("SenderId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_message_reaction_chat_members_sender_id");
|
|
||||||
|
|
||||||
b.Navigation("Message");
|
|
||||||
|
|
||||||
b.Navigation("Sender");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.MessageStatus", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.Message", "Message")
|
|
||||||
.WithMany("Statuses")
|
|
||||||
.HasForeignKey("MessageId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_chat_statuses_chat_messages_message_id");
|
|
||||||
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.ChatMember", "Sender")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("SenderId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_chat_statuses_chat_members_sender_id");
|
|
||||||
|
|
||||||
b.Navigation("Message");
|
|
||||||
|
|
||||||
b.Navigation("Sender");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroupMember", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroupMember", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DysonNetwork.Sphere.Permission.PermissionGroup", "Group")
|
b.HasOne("DysonNetwork.Sphere.Permission.PermissionGroup", "Group")
|
||||||
@ -2225,11 +1965,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasConstraintName("fk_files_accounts_account_id");
|
.HasConstraintName("fk_files_accounts_account_id");
|
||||||
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Chat.Message", null)
|
|
||||||
.WithMany("Attachments")
|
|
||||||
.HasForeignKey("MessageId")
|
|
||||||
.HasConstraintName("fk_files_chat_messages_message_id");
|
|
||||||
|
|
||||||
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
||||||
.WithMany("Attachments")
|
.WithMany("Attachments")
|
||||||
.HasForeignKey("PostId")
|
.HasForeignKey("PostId")
|
||||||
@ -2312,15 +2047,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.Navigation("Members");
|
b.Navigation("Members");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Chat.Message", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Attachments");
|
|
||||||
|
|
||||||
b.Navigation("Reactions");
|
|
||||||
|
|
||||||
b.Navigation("Statuses");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Permission.PermissionGroup", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Members");
|
b.Navigation("Members");
|
||||||
|
@ -51,7 +51,7 @@ public class Post : ModelBase
|
|||||||
public Post? ForwardedPost { get; set; }
|
public Post? ForwardedPost { get; set; }
|
||||||
public ICollection<CloudFile> Attachments { get; set; } = new List<CloudFile>();
|
public ICollection<CloudFile> Attachments { get; set; } = new List<CloudFile>();
|
||||||
|
|
||||||
[JsonIgnore] public NpgsqlTsVector? SearchVector { get; set; }
|
[JsonIgnore] public NpgsqlTsVector SearchVector { get; set; }
|
||||||
|
|
||||||
public Publisher Publisher { get; set; } = null!;
|
public Publisher Publisher { get; set; } = null!;
|
||||||
public ICollection<PostReaction> Reactions { get; set; } = new List<PostReaction>();
|
public ICollection<PostReaction> Reactions { get; set; } = new List<PostReaction>();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Casbin;
|
||||||
using DysonNetwork.Sphere.Account;
|
using DysonNetwork.Sphere.Account;
|
||||||
using DysonNetwork.Sphere.Permission;
|
using DysonNetwork.Sphere.Permission;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -74,9 +74,6 @@ public class PostService(AppDatabase db, FileService fs, ActivityService act)
|
|||||||
List<string>? categories = null
|
List<string>? categories = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (post.Empty)
|
|
||||||
throw new InvalidOperationException("Cannot create a post with barely no content.");
|
|
||||||
|
|
||||||
if (post.PublishedAt is not null)
|
if (post.PublishedAt is not null)
|
||||||
{
|
{
|
||||||
if (post.PublishedAt.Value.ToDateTimeUtc() < DateTime.UtcNow)
|
if (post.PublishedAt.Value.ToDateTimeUtc() < DateTime.UtcNow)
|
||||||
@ -121,27 +118,8 @@ public class PostService(AppDatabase db, FileService fs, ActivityService act)
|
|||||||
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vectorize the quill delta content
|
if (post.Empty)
|
||||||
if (post.Content?.RootElement is { ValueKind: JsonValueKind.Array })
|
throw new InvalidOperationException("Cannot create a post with barely no content.");
|
||||||
{
|
|
||||||
var searchTextBuilder = new System.Text.StringBuilder();
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(post.Title))
|
|
||||||
searchTextBuilder.AppendLine(post.Title);
|
|
||||||
if (!string.IsNullOrWhiteSpace(post.Description))
|
|
||||||
searchTextBuilder.AppendLine(post.Description);
|
|
||||||
|
|
||||||
foreach (var element in post.Content.RootElement.EnumerateArray())
|
|
||||||
{
|
|
||||||
if (element is { ValueKind: JsonValueKind.Object } &&
|
|
||||||
element.TryGetProperty("insert", out var insertProperty) &&
|
|
||||||
insertProperty.ValueKind == JsonValueKind.String)
|
|
||||||
{
|
|
||||||
searchTextBuilder.Append(insertProperty.GetString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post.SearchVector = EF.Functions.ToTsVector(searchTextBuilder.ToString().Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Notify the subscribers
|
// TODO Notify the subscribers
|
||||||
|
|
||||||
@ -162,9 +140,6 @@ public class PostService(AppDatabase db, FileService fs, ActivityService act)
|
|||||||
Instant? publishedAt = null
|
Instant? publishedAt = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (post.Empty)
|
|
||||||
throw new InvalidOperationException("Cannot edit a post to barely no content.");
|
|
||||||
|
|
||||||
post.EditedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
post.EditedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
|
|
||||||
if (publishedAt is not null)
|
if (publishedAt is not null)
|
||||||
@ -221,27 +196,8 @@ public class PostService(AppDatabase db, FileService fs, ActivityService act)
|
|||||||
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vectorize the quill delta content
|
if (post.Empty)
|
||||||
if (post.Content?.RootElement is { ValueKind: JsonValueKind.Array })
|
throw new InvalidOperationException("Cannot edit a post to barely no content.");
|
||||||
{
|
|
||||||
var searchTextBuilder = new System.Text.StringBuilder();
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(post.Title))
|
|
||||||
searchTextBuilder.AppendLine(post.Title);
|
|
||||||
if (!string.IsNullOrWhiteSpace(post.Description))
|
|
||||||
searchTextBuilder.AppendLine(post.Description);
|
|
||||||
|
|
||||||
foreach (var element in post.Content.RootElement.EnumerateArray())
|
|
||||||
{
|
|
||||||
if (element is { ValueKind: JsonValueKind.Object } &&
|
|
||||||
element.TryGetProperty("insert", out var insertProperty) &&
|
|
||||||
insertProperty.ValueKind == JsonValueKind.String)
|
|
||||||
{
|
|
||||||
searchTextBuilder.Append(insertProperty.GetString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post.SearchVector = EF.Functions.ToTsVector(searchTextBuilder.ToString().Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Update(post);
|
db.Update(post);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
@ -48,6 +48,6 @@ public class PublisherMember : ModelBase
|
|||||||
public long AccountId { get; set; }
|
public long AccountId { get; set; }
|
||||||
[JsonIgnore] public Account.Account Account { get; set; } = null!;
|
[JsonIgnore] public Account.Account Account { get; set; } = null!;
|
||||||
|
|
||||||
public PublisherMemberRole Role { get; set; } = PublisherMemberRole.Viewer;
|
public PublisherMemberRole Role { get; set; }
|
||||||
public Instant? JoinedAt { get; set; }
|
public Instant? JoinedAt { get; set; }
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Casbin;
|
||||||
using DysonNetwork.Sphere.Permission;
|
using DysonNetwork.Sphere.Permission;
|
||||||
using DysonNetwork.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
@ -3,17 +3,19 @@ using System.Security.Cryptography;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.RateLimiting;
|
using System.Threading.RateLimiting;
|
||||||
|
using Casbin;
|
||||||
|
using Casbin.Persist.Adapter.EFCore;
|
||||||
using DysonNetwork.Sphere;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Account;
|
using DysonNetwork.Sphere.Account;
|
||||||
using DysonNetwork.Sphere.Activity;
|
using DysonNetwork.Sphere.Activity;
|
||||||
using DysonNetwork.Sphere.Auth;
|
using DysonNetwork.Sphere.Auth;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Connection;
|
using DysonNetwork.Sphere.Connection;
|
||||||
using DysonNetwork.Sphere.Connection.Handlers;
|
|
||||||
using DysonNetwork.Sphere.Permission;
|
using DysonNetwork.Sphere.Permission;
|
||||||
using DysonNetwork.Sphere.Post;
|
using DysonNetwork.Sphere.Post;
|
||||||
using DysonNetwork.Sphere.Realm;
|
using DysonNetwork.Sphere.Realm;
|
||||||
using DysonNetwork.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
@ -116,10 +118,6 @@ builder.Services.AddSwaggerGen(options =>
|
|||||||
});
|
});
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
|
||||||
// The handlers for websocket
|
|
||||||
builder.Services.AddScoped<IWebSocketPacketHandler, MessageReadHandler>();
|
|
||||||
|
|
||||||
// Services
|
|
||||||
builder.Services.AddScoped<WebSocketService>();
|
builder.Services.AddScoped<WebSocketService>();
|
||||||
builder.Services.AddScoped<EmailService>();
|
builder.Services.AddScoped<EmailService>();
|
||||||
builder.Services.AddScoped<PermissionService>();
|
builder.Services.AddScoped<PermissionService>();
|
||||||
@ -134,7 +132,6 @@ builder.Services.AddScoped<ActivityService>();
|
|||||||
builder.Services.AddScoped<PostService>();
|
builder.Services.AddScoped<PostService>();
|
||||||
builder.Services.AddScoped<RealmService>();
|
builder.Services.AddScoped<RealmService>();
|
||||||
builder.Services.AddScoped<ChatRoomService>();
|
builder.Services.AddScoped<ChatRoomService>();
|
||||||
builder.Services.AddScoped<ChatService>();
|
|
||||||
|
|
||||||
// Timed task
|
// Timed task
|
||||||
|
|
||||||
@ -216,7 +213,7 @@ app.MapTus("/files/tus", _ => Task.FromResult<DefaultTusConfiguration>(new()
|
|||||||
}
|
}
|
||||||
|
|
||||||
var httpContext = eventContext.HttpContext;
|
var httpContext = eventContext.HttpContext;
|
||||||
if (httpContext.Items["CurrentUser"] is Account user)
|
var user = httpContext.Items["CurrentUser"] as Account;
|
||||||
if (user is null)
|
if (user is null)
|
||||||
{
|
{
|
||||||
eventContext.FailRequest(HttpStatusCode.Unauthorized);
|
eventContext.FailRequest(HttpStatusCode.Unauthorized);
|
||||||
|
@ -43,6 +43,6 @@ public class RealmMember : ModelBase
|
|||||||
public long AccountId { get; set; }
|
public long AccountId { get; set; }
|
||||||
[JsonIgnore] public Account.Account Account { get; set; } = null!;
|
[JsonIgnore] public Account.Account Account { get; set; } = null!;
|
||||||
|
|
||||||
public RealmMemberRole Role { get; set; } = RealmMemberRole.Normal;
|
public RealmMemberRole Role { get; set; }
|
||||||
public Instant? JoinedAt { get; set; }
|
public Instant? JoinedAt { get; set; }
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user