using System.Text; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Nuuru.Server.Data; using Nuuru.Server.Models; using Nuuru.Server.Utilities; namespace Nuuru.Server.Services { public interface IIpCloakService { Task CloakAsync(string ipAddress); Task UncloakAsync(string cloakedIp); Task> FindRawIpsByCloakedSubstringAsync(string substring); } public class IpCloakService : IIpCloakService { private readonly ApplicationDbContext _context; private readonly IMemoryCache _cache; private readonly byte[] _key; private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30); public IpCloakService(ApplicationDbContext context, IMemoryCache cache, IConfiguration configuration) { _context = context; _cache = cache; _key = Encoding.UTF8.GetBytes(configuration["IpCloak:Key"]!); } public async Task CloakAsync(string ipAddress) { var cacheKey = $"ipcloak:{ipAddress}"; if (_cache.TryGetValue(cacheKey, out string? cached)) return cached!; var cloaked = IpCloaker.Cloak(ipAddress, _key); _cache.Set(cacheKey, cloaked, CacheDuration); if (!await _context.IpCloakEntries.AnyAsync(e => e.RawIp == ipAddress)) { _context.IpCloakEntries.Add(new IpCloakEntry { RawIp = ipAddress, CloakedIp = cloaked, CreatedAt = DateTime.UtcNow }); await _context.SaveChangesAsync(); } return cloaked; } public async Task UncloakAsync(string cloakedIp) { var cacheKey = $"ipuncloak:{cloakedIp}"; if (_cache.TryGetValue(cacheKey, out string? cached)) return cached; var rawIp = await _context.IpCloakEntries .Where(e => e.CloakedIp == cloakedIp) .Select(e => e.RawIp) .FirstOrDefaultAsync(); if (rawIp != null) _cache.Set(cacheKey, rawIp, CacheDuration); return rawIp; } public async Task> FindRawIpsByCloakedSubstringAsync(string substring) { return await _context.IpCloakEntries .Where(e => e.CloakedIp.Contains(substring)) .Select(e => e.RawIp) .ToHashSetAsync(); } } }