273 lines
8.5 KiB
C#
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;
|
|
}
|
|
}
|