using System.Collections.Concurrent; using Microsoft.EntityFrameworkCore; using Yarp.ReverseProxy.Configuration; using YarpGateway.Data; using YarpGateway.Models; namespace YarpGateway.Config; public class DatabaseRouteConfigProvider { private readonly IDbContextFactory _dbContextFactory; private readonly ConcurrentDictionary _routes = new(); private readonly SemaphoreSlim _lock = new(1, 1); private readonly ILogger _logger; public DatabaseRouteConfigProvider( IDbContextFactory dbContextFactory, ILogger logger ) { _dbContextFactory = dbContextFactory; _logger = logger; _ = LoadConfigAsync(); } public IReadOnlyList GetRoutes() { return _routes.Values.ToList().AsReadOnly(); } public async Task ReloadAsync() { await _lock.WaitAsync(); try { await LoadConfigInternalAsync(); } finally { _lock.Release(); } } private async Task LoadConfigAsync() { await LoadConfigInternalAsync(); } private async Task LoadConfigInternalAsync() { await using var dbContext = _dbContextFactory.CreateDbContext(); var routes = await dbContext .TenantRoutes.Where(r => r.Status == 1 && !r.IsDeleted) .ToListAsync(); var newRoutes = new ConcurrentDictionary(); foreach (var route in routes) { var config = new RouteConfig { RouteId = route.Id.ToString(), ClusterId = route.ClusterId, Match = new RouteMatch { Path = route.PathPattern }, Metadata = new Dictionary { ["TenantCode"] = route.TenantCode, ["ServiceName"] = route.ServiceName, }, }; newRoutes[route.Id.ToString()] = config; } _routes.Clear(); foreach (var route in newRoutes) { _routes[route.Key] = route.Value; } _logger.LogInformation("Loaded {Count} routes from database", _routes.Count); } }