fengling-console/src/YarpGateway/Controllers/GatewayConfigController.cs

273 lines
8.5 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using YarpGateway.Data;
using YarpGateway.Config;
using YarpGateway.Models;
using YarpGateway.Services;
using Yarp.ReverseProxy.Configuration;
namespace YarpGateway.Controllers;
[ApiController]
[Route("api/gateway")]
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;
}
[HttpGet("tenants")]
public async Task<IActionResult> GetTenants()
{
await using var db = _dbContextFactory.CreateDbContext();
var tenants = await db.Tenants
.Where(t => !t.IsDeleted)
.ToListAsync();
return Ok(tenants);
}
[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 GwTenant
{
Id = GenerateId(),
TenantCode = dto.TenantCode,
TenantName = dto.TenantName,
Status = 1
};
await db.Tenants.AddAsync(tenant);
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;
await db.SaveChangesAsync();
return Ok();
}
[HttpGet("tenants/{tenantCode}/routes")]
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);
}
[HttpPost("tenants/{tenantCode}/routes")]
public async Task<IActionResult> CreateTenantRoute(string tenantCode, [FromBody] CreateTenantRouteDto dto)
{
await using var db = _dbContextFactory.CreateDbContext();
var tenant = await db.Tenants
.FirstOrDefaultAsync(t => t.TenantCode == tenantCode);
if (tenant == null)
return BadRequest($"Tenant {tenantCode} not found");
var clusterId = $"{tenantCode}-{dto.ServiceName}";
var existing = await db.TenantRoutes
.FirstOrDefaultAsync(r => r.ClusterId == clusterId);
if (existing != null)
return BadRequest($"Route for {tenantCode}/{dto.ServiceName} already exists");
var route = new GwTenantRoute
{
Id = GenerateId(),
TenantCode = tenantCode,
ServiceName = dto.ServiceName,
ClusterId = clusterId,
PathPattern = dto.PathPattern,
Priority = 10,
Status = 1,
IsGlobal = false
};
await db.TenantRoutes.AddAsync(route);
await db.SaveChangesAsync();
await _routeCache.ReloadAsync();
return Ok(route);
}
[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);
}
[HttpPost("routes/global")]
public async Task<IActionResult> CreateGlobalRoute([FromBody] CreateGlobalRouteDto dto)
{
await using var db = _dbContextFactory.CreateDbContext();
var existing = await db.TenantRoutes
.FirstOrDefaultAsync(r => r.ServiceName == dto.ServiceName && r.IsGlobal);
if (existing != null)
{
return BadRequest($"Global route for {dto.ServiceName} already exists");
}
var route = new GwTenantRoute
{
Id = GenerateId(),
TenantCode = string.Empty,
ServiceName = dto.ServiceName,
ClusterId = dto.ClusterId,
PathPattern = dto.PathPattern,
Priority = 0,
Status = 1,
IsGlobal = true
};
await db.TenantRoutes.AddAsync(route);
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;
await db.SaveChangesAsync();
await _routeCache.ReloadAsync();
return Ok();
}
[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);
}
[HttpPost("clusters/{clusterId}/instances")]
public async Task<IActionResult> AddInstance(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 in cluster {clusterId}");
var instance = new GwServiceInstance
{
Id = GenerateId(),
ClusterId = clusterId,
DestinationId = dto.DestinationId,
Address = dto.Address,
Weight = dto.Weight,
Health = 1,
Status = 1
};
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;
await db.SaveChangesAsync();
await _clusterProvider.ReloadAsync();
return Ok();
}
[HttpPost("reload")]
public async Task<IActionResult> ReloadConfig()
{
await _routeCache.ReloadAsync();
await _routeProvider.ReloadAsync();
await _clusterProvider.ReloadAsync();
return Ok(new { message = "Config reloaded successfully" });
}
private long GenerateId()
{
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
public class CreateTenantDto
{
public string TenantCode { get; set; } = string.Empty;
public string TenantName { get; set; } = string.Empty;
}
public class CreateTenantRouteDto
{
public string ServiceName { get; set; } = string.Empty;
public string PathPattern { get; set; } = string.Empty;
}
public class CreateGlobalRouteDto
{
public string ServiceName { get; set; } = string.Empty;
public string ClusterId { get; set; } = string.Empty;
public string PathPattern { get; set; } = string.Empty;
}
public class CreateInstanceDto
{
public string DestinationId { get; set; } = string.Empty;
public string Address { get; set; } = string.Empty;
public int Weight { get; set; } = 1;
}
}