using Application.Abstractions.Cache;
using Application.Abstractions.Chat;
using Application.Abstractions.Hub;
using StackExchange.Redis;
using System.Text.Json;
namespace Infrastructure.Hubs;
///
/// AppHub 전역 접속자 추적 (Redis 기반).
/// 단일 Hash `app:connections` 에 connectionId → ConnectedUser JSON 으로 기록 (게스트 포함).
///
internal sealed class RedisVisitorTracker(IConnectionMultiplexer redis) : IVisitorTracker
{
private readonly IDatabase _db = redis.GetDatabase();
private static readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
public async Task AddAsync(ConnectedUser user)
{
var json = JsonSerializer.Serialize(user, _jsonOptions);
await _db.HashSetAsync(CacheKeys.AppConnections, user.ConnectionId, json);
}
public async Task RemoveAsync(string connectionId)
{
await _db.HashDeleteAsync(CacheKeys.AppConnections, connectionId);
}
public async Task> GetAllAsync()
{
var entries = await _db.HashGetAllAsync(CacheKeys.AppConnections);
if (entries.Length <= 0)
{
return [];
}
var list = new List(entries.Length);
foreach (var entry in entries)
{
if (!entry.Value.IsNullOrEmpty)
{
var user = JsonSerializer.Deserialize((string)entry.Value!, _jsonOptions);
if (user is not null)
{
list.Add(user);
}
}
}
return list.AsReadOnly();
}
public async Task ClearAllAsync()
{
// 서버 시작 시 전체 초기화 (stale 제거) — 단일 키라 SCAN 불필요
await _db.KeyDeleteAsync(CacheKeys.AppConnections);
}
}