- Add 4 PG entities: VerificationCode, RateLimitEntry, TokenBlacklistEntry, CacheEntry - Add 4 services: VerificationService, RateLimitService, TokenBlacklistService, CacheService - Add CleanupBackgroundService for periodic expired data cleanup - Add MigrationHelper for safe schema migration without data loss - Update AuthController: real SMS code generation, rate limiting, logout endpoint with JWT blacklist - Update JwtProvider: add JTI claim for token revocation - Update Program.cs: register new services, JWT blacklist validation, DB migration - Remove StackExchange.Redis NuGet package and all Redis config references - Update start-dev.bat: 6→5 services, remove Redis startup - Update docs: remove Redis references from all documentation - Fix: logout button spacing on profile page - Fix: .gitignore data/→/data/ to not ignore Infrastructure/Data/
55 lines
1.7 KiB
C#
55 lines
1.7 KiB
C#
using System.Text.Json;
|
|
using HealthManager.Domain.Entities;
|
|
using HealthManager.Infrastructure.Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace HealthManager.Application.Services;
|
|
|
|
public class CacheService(AppDbContext db)
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
|
|
|
|
public async Task<T?> GetAsync<T>(string key) where T : class
|
|
{
|
|
var entry = await db.CacheEntries
|
|
.FirstOrDefaultAsync(c => c.Key == key && c.ExpiresAt > DateTime.UtcNow);
|
|
if (entry == null) return default;
|
|
return JsonSerializer.Deserialize<T>(entry.Value.RootElement.GetRawText(), JsonOptions);
|
|
}
|
|
|
|
public async Task SetAsync<T>(string key, T value, TimeSpan ttl)
|
|
{
|
|
var json = JsonSerializer.SerializeToDocument(value);
|
|
var existing = await db.CacheEntries.FirstOrDefaultAsync(c => c.Key == key);
|
|
if (existing != null)
|
|
{
|
|
existing.Value = json;
|
|
existing.ExpiresAt = DateTime.UtcNow.Add(ttl);
|
|
}
|
|
else
|
|
{
|
|
db.CacheEntries.Add(new CacheEntry
|
|
{
|
|
Key = key,
|
|
Value = json,
|
|
ExpiresAt = DateTime.UtcNow.Add(ttl),
|
|
});
|
|
}
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, TimeSpan ttl) where T : class
|
|
{
|
|
var cached = await GetAsync<T>(key);
|
|
if (cached != null) return cached;
|
|
var value = await factory();
|
|
await SetAsync(key, value, ttl);
|
|
return value;
|
|
}
|
|
|
|
public async Task RemoveAsync(string key)
|
|
{
|
|
await db.CacheEntries.Where(c => c.Key == key).ExecuteDeleteAsync();
|
|
}
|
|
}
|