using Microsoft.EntityFrameworkCore; using Nexus.Api.Data; namespace Nexus.Api.Repositories; public sealed class UserRepository(NexusDbContext db) : IUserRepository { public ValueTask GetByIdAsync(Guid userId, CancellationToken ct = default) => db.Users.FindAsync([userId], ct); public Task GetByEmailAsync(string normalizedEmail, CancellationToken ct = default) => db.Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, ct); public Task AnyUsersAsync(CancellationToken ct = default) => db.Users.AnyAsync(ct); public async Task AddAsync(NexusUser user, CancellationToken ct = default) { db.Users.Add(user); await db.SaveChangesAsync(ct); return user; } public Task UpdateAsync(NexusUser user, CancellationToken ct = default) => db.SaveChangesAsync(ct); public Task GetRefreshTokenByHashAsync(string tokenHash, CancellationToken ct = default) => db.RefreshTokens .Include(r => r.User) .FirstOrDefaultAsync(r => r.TokenHash == tokenHash, ct); public Task> GetActiveTokensByFamilyAsync(Guid familyId, CancellationToken ct = default) => db.RefreshTokens .Where(r => r.FamilyId == familyId && r.RevokedAt == null) .ToListAsync(ct); public async Task AddRefreshTokenAsync(RefreshToken token, CancellationToken ct = default) { db.RefreshTokens.Add(token); await db.SaveChangesAsync(ct); } public Task UpdateRefreshTokenAsync(RefreshToken token, CancellationToken ct = default) => db.SaveChangesAsync(ct); public async Task RemoveExpiredTokensAsync(Guid userId, CancellationToken ct = default) { var cutoff = DateTimeOffset.UtcNow.AddDays(-30); var oldTokens = await db.RefreshTokens .Where(r => r.UserId == userId && (r.ExpiresAt < DateTimeOffset.UtcNow || r.RevokedAt < cutoff)) .ToListAsync(ct); if (oldTokens.Count > 0) db.RefreshTokens.RemoveRange(oldTokens); } public Task SaveChangesAsync(CancellationToken ct = default) => db.SaveChangesAsync(ct); }