refactor: 移除 GatewayConfigController - 网关只需转发请求
This commit is contained in:
parent
0c08620565
commit
449fe3a385
@ -1,491 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using YarpGateway.Data;
|
||||
using YarpGateway.Config;
|
||||
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
||||
using YarpGateway.Services;
|
||||
|
||||
namespace YarpGateway.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/gateway")]
|
||||
[Authorize] // 要求所有管理 API 都需要认证
|
||||
public class GatewayConfigController : ControllerBase
|
||||
{
|
||||
private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
|
||||
private readonly DatabaseRouteConfigProvider _routeProvider;
|
||||
private readonly DatabaseClusterConfigProvider _clusterProvider;
|
||||
private readonly IRouteCache _routeCache;
|
||||
|
||||
public GatewayConfigController(
|
||||
IDbContextFactory<GatewayDbContext> dbContextFactory,
|
||||
DatabaseRouteConfigProvider routeProvider,
|
||||
DatabaseClusterConfigProvider clusterProvider,
|
||||
IRouteCache routeCache)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_routeProvider = routeProvider;
|
||||
_clusterProvider = clusterProvider;
|
||||
_routeCache = routeCache;
|
||||
}
|
||||
|
||||
#region Tenants
|
||||
|
||||
[HttpGet("tenants")]
|
||||
public async Task<IActionResult> GetTenants([FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string? keyword = null)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var query = db.Tenants.Where(t => !t.IsDeleted);
|
||||
|
||||
if (!string.IsNullOrEmpty(keyword))
|
||||
{
|
||||
query = query.Where(t => t.TenantCode.Contains(keyword) || t.TenantName.Contains(keyword));
|
||||
}
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query
|
||||
.OrderByDescending(t => t.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(t => new
|
||||
{
|
||||
t.Id,
|
||||
t.TenantCode,
|
||||
t.TenantName,
|
||||
t.Status,
|
||||
RouteCount = db.TenantRoutes.Count(r => r.TenantCode == t.TenantCode && !r.IsDeleted),
|
||||
t.Version,
|
||||
t.CreatedTime,
|
||||
t.UpdatedTime
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(new { items, total, page, pageSize, totalPages = (int)Math.Ceiling(total / (double)pageSize) });
|
||||
}
|
||||
|
||||
[HttpGet("tenants/{id}")]
|
||||
public async Task<IActionResult> GetTenant(long id)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var tenant = await db.Tenants.FindAsync(id);
|
||||
if (tenant == null) return NotFound();
|
||||
return Ok(tenant);
|
||||
}
|
||||
|
||||
[HttpPost("tenants")]
|
||||
public async Task<IActionResult> CreateTenant([FromBody] CreateTenantDto dto)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var existing = await db.Tenants.FirstOrDefaultAsync(t => t.TenantCode == dto.TenantCode);
|
||||
if (existing != null) return BadRequest($"Tenant code {dto.TenantCode} already exists");
|
||||
|
||||
var tenant = new Tenant
|
||||
{
|
||||
Id = GenerateId(),
|
||||
TenantCode = dto.TenantCode,
|
||||
TenantName = dto.TenantName,
|
||||
Status = 1,
|
||||
Version = 1
|
||||
};
|
||||
await db.Tenants.AddAsync(tenant);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return Ok(tenant);
|
||||
}
|
||||
|
||||
[HttpPut("tenants/{id}")]
|
||||
public async Task<IActionResult> UpdateTenant(long id, [FromBody] UpdateTenantDto dto)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var tenant = await db.Tenants.FindAsync(id);
|
||||
if (tenant == null) return NotFound();
|
||||
|
||||
if (!string.IsNullOrEmpty(dto.TenantName)) tenant.TenantName = dto.TenantName;
|
||||
if (dto.Status != null) tenant.Status = dto.Status.Value;
|
||||
|
||||
tenant.Version++;
|
||||
tenant.UpdatedTime = DateTime.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return Ok(tenant);
|
||||
}
|
||||
|
||||
[HttpDelete("tenants/{id}")]
|
||||
public async Task<IActionResult> DeleteTenant(long id)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var tenant = await db.Tenants.FindAsync(id);
|
||||
if (tenant == null) return NotFound();
|
||||
|
||||
tenant.IsDeleted = true;
|
||||
tenant.UpdatedTime = DateTime.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Routes
|
||||
|
||||
[HttpGet("routes")]
|
||||
public async Task<IActionResult> GetRoutes([FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string? tenantCode = null, [FromQuery] bool? isGlobal = null)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var query = db.TenantRoutes.Where(r => !r.IsDeleted);
|
||||
|
||||
if (!string.IsNullOrEmpty(tenantCode))
|
||||
query = query.Where(r => r.TenantCode == tenantCode);
|
||||
if (isGlobal != null)
|
||||
query = query.Where(r => r.IsGlobal == isGlobal.Value);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query
|
||||
.OrderBy(r => r.Priority)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(new { items, total, page, pageSize, totalPages = (int)Math.Ceiling(total / (double)pageSize) });
|
||||
}
|
||||
|
||||
[HttpGet("routes/global")]
|
||||
public async Task<IActionResult> GetGlobalRoutes()
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var routes = await db.TenantRoutes.Where(r => r.IsGlobal && !r.IsDeleted).ToListAsync();
|
||||
return Ok(routes);
|
||||
}
|
||||
|
||||
[HttpGet("routes/tenant/{tenantCode}")]
|
||||
public async Task<IActionResult> GetTenantRoutes(string tenantCode)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var routes = await db.TenantRoutes.Where(r => r.TenantCode == tenantCode && !r.IsDeleted).ToListAsync();
|
||||
return Ok(routes);
|
||||
}
|
||||
|
||||
[HttpGet("routes/{id}")]
|
||||
public async Task<IActionResult> GetRoute(long id)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var route = await db.TenantRoutes.FindAsync(id);
|
||||
if (route == null) return NotFound();
|
||||
return Ok(route);
|
||||
}
|
||||
|
||||
[HttpPost("routes")]
|
||||
public async Task<IActionResult> CreateRoute([FromBody] CreateRouteDto dto)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
|
||||
if ((dto.IsGlobal != true) && !string.IsNullOrEmpty(dto.TenantCode))
|
||||
{
|
||||
var tenant = await db.Tenants.FirstOrDefaultAsync(t => t.TenantCode == dto.TenantCode);
|
||||
if (tenant == null) return BadRequest($"Tenant {dto.TenantCode} not found");
|
||||
}
|
||||
|
||||
var route = new TenantRoute
|
||||
{
|
||||
Id = GenerateId(),
|
||||
TenantCode = dto.TenantCode ?? string.Empty,
|
||||
ServiceName = dto.ServiceName,
|
||||
ClusterId = dto.ClusterId,
|
||||
PathPattern = dto.PathPattern,
|
||||
Priority = dto.Priority ?? 10,
|
||||
Status = 1,
|
||||
IsGlobal = dto.IsGlobal ?? false,
|
||||
Version = 1,
|
||||
CreatedTime = DateTime.UtcNow
|
||||
};
|
||||
await db.TenantRoutes.AddAsync(route);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await _routeCache.ReloadAsync();
|
||||
|
||||
return Ok(route);
|
||||
}
|
||||
|
||||
[HttpPut("routes/{id}")]
|
||||
public async Task<IActionResult> UpdateRoute(long id, [FromBody] CreateRouteDto dto)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var route = await db.TenantRoutes.FindAsync(id);
|
||||
if (route == null) return NotFound();
|
||||
|
||||
route.ServiceName = dto.ServiceName;
|
||||
route.ClusterId = dto.ClusterId;
|
||||
route.PathPattern = dto.PathPattern;
|
||||
if (dto.Priority != null) route.Priority = dto.Priority.Value;
|
||||
route.Version++;
|
||||
route.UpdatedTime = DateTime.UtcNow;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await _routeCache.ReloadAsync();
|
||||
|
||||
return Ok(route);
|
||||
}
|
||||
|
||||
[HttpDelete("routes/{id}")]
|
||||
public async Task<IActionResult> DeleteRoute(long id)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var route = await db.TenantRoutes.FindAsync(id);
|
||||
if (route == null) return NotFound();
|
||||
|
||||
route.IsDeleted = true;
|
||||
route.UpdatedTime = DateTime.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await _routeCache.ReloadAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Clusters
|
||||
|
||||
[HttpGet("clusters")]
|
||||
public async Task<IActionResult> GetClusters()
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var clusters = await db.ServiceInstances
|
||||
.Where(i => !i.IsDeleted)
|
||||
.GroupBy(i => i.ClusterId)
|
||||
.Select(g => new
|
||||
{
|
||||
ClusterId = g.Key,
|
||||
ClusterName = g.Key,
|
||||
InstanceCount = g.Count(),
|
||||
HealthyInstanceCount = g.Count(i => i.Health == 1),
|
||||
Instances = g.ToList()
|
||||
})
|
||||
.ToListAsync();
|
||||
return Ok(clusters);
|
||||
}
|
||||
|
||||
[HttpGet("clusters/{clusterId}")]
|
||||
public async Task<IActionResult> GetCluster(string clusterId)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var instances = await db.ServiceInstances.Where(i => i.ClusterId == clusterId && !i.IsDeleted).ToListAsync();
|
||||
if (!instances.Any()) return NotFound();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
ClusterId = clusterId,
|
||||
ClusterName = clusterId,
|
||||
InstanceCount = instances.Count,
|
||||
HealthyInstanceCount = instances.Count(i => i.Health == 1),
|
||||
Instances = instances
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("clusters")]
|
||||
public async Task<IActionResult> CreateCluster([FromBody] CreateClusterDto dto)
|
||||
{
|
||||
return Ok(new { message = "Cluster created", clusterId = dto.ClusterId });
|
||||
}
|
||||
|
||||
[HttpDelete("clusters/{clusterId}")]
|
||||
public async Task<IActionResult> DeleteCluster(string clusterId)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var instances = await db.ServiceInstances.Where(i => i.ClusterId == clusterId).ToListAsync();
|
||||
foreach (var instance in instances)
|
||||
{
|
||||
instance.IsDeleted = true;
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
await _clusterProvider.ReloadAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Instances
|
||||
|
||||
[HttpGet("clusters/{clusterId}/instances")]
|
||||
public async Task<IActionResult> GetInstances(string clusterId)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var instances = await db.ServiceInstances.Where(i => i.ClusterId == clusterId && !i.IsDeleted).ToListAsync();
|
||||
return Ok(instances);
|
||||
}
|
||||
|
||||
[HttpGet("instances/{id}")]
|
||||
public async Task<IActionResult> GetInstance(long id)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var instance = await db.ServiceInstances.FindAsync(id);
|
||||
if (instance == null) return NotFound();
|
||||
return Ok(instance);
|
||||
}
|
||||
|
||||
[HttpPost("clusters/{clusterId}/instances")]
|
||||
public async Task<IActionResult> CreateInstance(string clusterId, [FromBody] CreateInstanceDto dto)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var existing = await db.ServiceInstances.FirstOrDefaultAsync(i => i.ClusterId == clusterId && i.DestinationId == dto.DestinationId);
|
||||
if (existing != null) return BadRequest($"Instance {dto.DestinationId} already exists");
|
||||
|
||||
var instance = new GwServiceInstance
|
||||
{
|
||||
Id = GenerateId(),
|
||||
ClusterId = clusterId,
|
||||
DestinationId = dto.DestinationId,
|
||||
Address = dto.Address,
|
||||
Weight = dto.Weight ?? 1,
|
||||
Health = dto.IsHealthy == true ? 1 : 0,
|
||||
Status = 1,
|
||||
Version = 1,
|
||||
CreatedTime = DateTime.UtcNow
|
||||
};
|
||||
await db.ServiceInstances.AddAsync(instance);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await _clusterProvider.ReloadAsync();
|
||||
|
||||
return Ok(instance);
|
||||
}
|
||||
|
||||
[HttpDelete("instances/{id}")]
|
||||
public async Task<IActionResult> DeleteInstance(long id)
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var instance = await db.ServiceInstances.FindAsync(id);
|
||||
if (instance == null) return NotFound();
|
||||
|
||||
instance.IsDeleted = true;
|
||||
instance.UpdatedTime = DateTime.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await _clusterProvider.ReloadAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Config & Stats
|
||||
|
||||
[HttpPost("config/reload")]
|
||||
public async Task<IActionResult> ReloadConfig()
|
||||
{
|
||||
await _routeCache.ReloadAsync();
|
||||
await _routeProvider.ReloadAsync();
|
||||
await _clusterProvider.ReloadAsync();
|
||||
return Ok(new { message = "Config reloaded successfully", timestamp = DateTime.UtcNow });
|
||||
}
|
||||
|
||||
[HttpGet("config/status")]
|
||||
public async Task<IActionResult> GetConfigStatus()
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var routeCount = await db.TenantRoutes.CountAsync(r => r.Status == 1 && !r.IsDeleted);
|
||||
var instanceCount = await db.ServiceInstances.CountAsync(i => i.Status == 1 && !i.IsDeleted);
|
||||
var healthyCount = await db.ServiceInstances.CountAsync(i => i.Health == 1 && !i.IsDeleted);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
routeCount,
|
||||
clusterCount = await db.ServiceInstances.Where(i => !i.IsDeleted).GroupBy(i => i.ClusterId).CountAsync(),
|
||||
instanceCount,
|
||||
healthyInstanceCount = healthyCount,
|
||||
lastReloadTime = DateTime.UtcNow,
|
||||
isListening = true,
|
||||
listenerStatus = "Active"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("config/versions")]
|
||||
public async Task<IActionResult> GetVersionInfo()
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var routeVersion = await db.TenantRoutes.OrderByDescending(r => r.Version).Select(r => r.Version).FirstOrDefaultAsync();
|
||||
var clusterVersion = await db.ServiceInstances.OrderByDescending(i => i.Version).Select(i => i.Version).FirstOrDefaultAsync();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
routeVersion,
|
||||
clusterVersion,
|
||||
routeVersionUpdatedAt = DateTime.UtcNow,
|
||||
clusterVersionUpdatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("stats/overview")]
|
||||
public async Task<IActionResult> GetOverviewStats()
|
||||
{
|
||||
await using var db = _dbContextFactory.CreateDbContext();
|
||||
var totalTenants = await db.Tenants.CountAsync(t => !t.IsDeleted);
|
||||
var activeTenants = await db.Tenants.CountAsync(t => !t.IsDeleted && t.Status == 1);
|
||||
var totalRoutes = await db.TenantRoutes.CountAsync(r => r.Status == 1 && !r.IsDeleted);
|
||||
var totalInstances = await db.ServiceInstances.CountAsync(i => i.Status == 1 && !i.IsDeleted);
|
||||
var healthyInstances = await db.ServiceInstances.CountAsync(i => i.Health == 1 && !i.IsDeleted);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
totalTenants,
|
||||
activeTenants,
|
||||
totalRoutes,
|
||||
totalClusters = await db.ServiceInstances.Where(i => !i.IsDeleted).GroupBy(i => i.ClusterId).CountAsync(),
|
||||
totalInstances,
|
||||
healthyInstances,
|
||||
lastUpdated = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DTOs
|
||||
|
||||
public class CreateTenantDto
|
||||
{
|
||||
public string TenantCode { get; set; } = string.Empty;
|
||||
public string TenantName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UpdateTenantDto
|
||||
{
|
||||
public string? TenantName { get; set; }
|
||||
public int? Status { get; set; }
|
||||
}
|
||||
|
||||
public class CreateRouteDto
|
||||
{
|
||||
public string? TenantCode { get; set; }
|
||||
public string ServiceName { get; set; } = string.Empty;
|
||||
public string ClusterId { get; set; } = string.Empty;
|
||||
public string PathPattern { get; set; } = string.Empty;
|
||||
public int? Priority { get; set; }
|
||||
public bool? IsGlobal { get; set; }
|
||||
}
|
||||
|
||||
public class CreateClusterDto
|
||||
{
|
||||
public string ClusterId { get; set; } = string.Empty;
|
||||
public string ClusterName { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public string? LoadBalancingPolicy { get; set; }
|
||||
}
|
||||
|
||||
public class CreateInstanceDto
|
||||
{
|
||||
public string DestinationId { get; set; } = string.Empty;
|
||||
public string Address { get; set; } = string.Empty;
|
||||
public int? Weight { get; set; }
|
||||
public bool? IsHealthy { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private long GenerateId()
|
||||
{
|
||||
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user