| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- using Application.Abstractions.Chat;
- using Application.Abstractions.Data;
- using Microsoft.AspNetCore.Authorization;
- using Microsoft.AspNetCore.SignalR;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.IdentityModel.JsonWebTokens;
- using System.Collections.Concurrent;
- namespace Web.Api.Hubs;
- [Authorize]
- public sealed class ChatHub(IChatMessageStore messageStore, IServiceScopeFactory scopeFactory) : Hub<IChatHubClient>
- {
- private static readonly ConcurrentDictionary<string, DateTime> _lastMessageTime = new();
- public override async Task OnConnectedAsync()
- {
- var messages = await messageStore.GetRecentMessagesAsync(ChatSettings.MaxMessages);
- await Clients.Caller.ReceiveHistory(messages);
- var memberName = GetMemberName();
- if (!string.IsNullOrEmpty(memberName))
- {
- await Clients.Others.ReceiveSystemMessage($"{memberName}님이 입장했습니다.");
- }
- await base.OnConnectedAsync();
- }
- public override async Task OnDisconnectedAsync(Exception? exception)
- {
- _lastMessageTime.TryRemove(Context.ConnectionId, out _);
- var memberName = GetMemberName();
- if (!string.IsNullOrEmpty(memberName))
- {
- await Clients.Others.ReceiveSystemMessage($"{memberName}님이 퇴장했습니다.");
- }
- await base.OnDisconnectedAsync(exception);
- }
- public async Task SendMessage(string content)
- {
- if (string.IsNullOrWhiteSpace(content))
- {
- return;
- }
- content = content.Trim();
- if (content.Length > ChatSettings.MaxContentLength)
- {
- return;
- }
- var now = DateTime.UtcNow;
- if (_lastMessageTime.TryGetValue(Context.ConnectionId, out var lastTime))
- {
- if ((now - lastTime).TotalSeconds < ChatSettings.RateLimitSeconds)
- {
- return;
- }
- }
- _lastMessageTime[Context.ConnectionId] = now;
- var memberID = GetMemberID();
- if (memberID is null)
- {
- return;
- }
- using var scope = scopeFactory.CreateScope();
- var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
- var member = await db.Member.AsNoTracking().Where(x => x.ID == memberID.Value).Select(x => new { x.SID, x.Name }).FirstOrDefaultAsync();
- if (member is null)
- {
- return;
- }
- var message = new ChatMessage(
- memberID.Value,
- member.SID,
- member.Name ?? "익명",
- content,
- now
- );
- // 채팅 기록 저장
- await messageStore.AddMessageAsync(message);
- // 모든 클라이언트에게 메시지 전송
- await Clients.All.ReceiveMessage(message);
- }
- private int? GetMemberID()
- {
- var sub = Context.User?.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
- if (int.TryParse(sub, out var memberID))
- {
- return memberID;
- }
- return null;
- }
- private string? GetMemberName()
- {
- return Context.User?.FindFirst(JwtRegisteredClaimNames.Name)?.Value;
- }
- }
|