diff --git a/Controllers/GatewayController.cs b/Controllers/GatewayController.cs
new file mode 100644
index 0000000..7c58dcb
--- /dev/null
+++ b/Controllers/GatewayController.cs
@@ -0,0 +1,361 @@
+namespace Fengling.Console.Controllers;
+
+///
+/// 网关管理控制器
+/// 提供网关服务、路由、集群实例等配置管理功能
+///
+[ApiController]
+[Route("api/console/[controller]")]
+[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
+public class GatewayController(IGatewayService gatewayService, ILogger logger)
+ : ControllerBase
+{
+ ///
+ /// 获取网关统计数据
+ ///
+ /// 网关的整体统计信息,包括请求量、响应时间等指标
+ /// 成功返回网关统计数据
+ /// 服务器内部错误
+ [HttpGet("statistics")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(GatewayStatisticsDto), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task> GetStatistics()
+ {
+ try
+ {
+ var statistics = await gatewayService.GetStatisticsAsync();
+ return Ok(statistics);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error getting gateway statistics");
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 获取网关服务列表
+ ///
+ /// 是否只返回全局服务,默认为false
+ /// 租户编码,用于筛选特定租户的服务
+ /// 网关服务列表,包含服务名称、地址、健康状态等信息
+ /// 成功返回网关服务列表
+ /// 服务器内部错误
+ [HttpGet("services")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task>> GetServices(
+ [FromQuery] bool globalOnly = false,
+ [FromQuery] string? tenantCode = null)
+ {
+ try
+ {
+ var services = await gatewayService.GetServicesAsync(globalOnly, tenantCode);
+ return Ok(services);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error getting gateway services");
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 获取单个网关服务详情
+ ///
+ /// 服务名称
+ /// 租户编码,用于筛选特定租户的服务
+ /// 网关服务的详细信息
+ /// 成功返回服务详情
+ /// 服务不存在
+ /// 服务器内部错误
+ [HttpGet("services/{serviceName}")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(GatewayServiceDto), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task> GetService(
+ string serviceName,
+ [FromQuery] string? tenantCode = null)
+ {
+ try
+ {
+ var service = await gatewayService.GetServiceAsync(serviceName, tenantCode);
+ if (service == null)
+ {
+ return NotFound(new { message = $"Service {serviceName} not found" });
+ }
+ return Ok(service);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error getting service {ServiceName}", serviceName);
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 注册新的网关服务
+ ///
+ /// 创建服务所需的配置信息
+ /// 创建的服务详情
+ /// 成功创建服务
+ /// 请求参数无效或服务已存在
+ /// 服务器内部错误
+ [HttpPost("services")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(GatewayServiceDto), StatusCodes.Status201Created)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task> RegisterService([FromBody] CreateGatewayServiceDto dto)
+ {
+ try
+ {
+ var service = await gatewayService.RegisterServiceAsync(dto);
+ return CreatedAtAction(nameof(GetService), new { serviceName = service.ServicePrefix }, service);
+ }
+ catch (InvalidOperationException ex)
+ {
+ logger.LogWarning(ex, "Validation error registering service");
+ return BadRequest(new { message = ex.Message });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error registering service");
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 注销网关服务
+ ///
+ /// 要注销的服务名称
+ /// 租户编码,用于筛选特定租户的服务
+ /// 无内容响应
+ /// 成功注销服务
+ /// 服务不存在
+ /// 服务器内部错误
+ [HttpDelete("services/{serviceName}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task UnregisterService(
+ string serviceName,
+ [FromQuery] string? tenantCode = null)
+ {
+ try
+ {
+ var result = await gatewayService.UnregisterServiceAsync(serviceName, tenantCode);
+ if (!result)
+ {
+ return NotFound(new { message = $"Service {serviceName} not found" });
+ }
+ return Ok(new { message = "Service unregistered successfully" });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error unregistering service {ServiceName}", serviceName);
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 获取网关路由列表
+ ///
+ /// 是否只返回全局路由,默认为false
+ /// 网关路由列表,包含路径匹配规则、转发目标等信息
+ /// 成功返回网关路由列表
+ /// 服务器内部错误
+ [HttpGet("routes")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task>> GetRoutes([FromQuery] bool globalOnly = false)
+ {
+ try
+ {
+ var routes = await gatewayService.GetRoutesAsync(globalOnly);
+ return Ok(routes);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error getting gateway routes");
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 创建新的网关路由
+ ///
+ /// 创建路由所需的配置信息
+ /// 创建的路由详情
+ /// 成功创建路由
+ /// 请求参数无效
+ /// 服务器内部错误
+ [HttpPost("routes")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(GatewayRouteDto), StatusCodes.Status201Created)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task> CreateRoute([FromBody] CreateGatewayRouteDto dto)
+ {
+ try
+ {
+ var route = await gatewayService.CreateRouteAsync(dto);
+ return Ok(route);
+ }
+ catch (InvalidOperationException ex)
+ {
+ return BadRequest(new { message = ex.Message });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error creating gateway route");
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 获取集群实例列表
+ ///
+ /// 集群ID
+ /// 指定集群下的所有服务实例列表
+ /// 成功返回实例列表
+ /// 服务器内部错误
+ [HttpGet("clusters/{clusterId}/instances")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task>> GetInstances(string clusterId)
+ {
+ try
+ {
+ var instances = await gatewayService.GetInstancesAsync(clusterId);
+ return Ok(instances);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error getting instances for cluster {ClusterId}", clusterId);
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 添加服务实例到集群
+ ///
+ /// 创建实例所需的配置信息
+ /// 创建的实例详情
+ /// 成功添加实例
+ /// 请求参数无效
+ /// 服务器内部错误
+ [HttpPost("instances")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(GatewayInstanceDto), StatusCodes.Status201Created)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task> AddInstance([FromBody] CreateGatewayInstanceDto dto)
+ {
+ try
+ {
+ var instance = await gatewayService.AddInstanceAsync(dto);
+ return CreatedAtAction(nameof(GetInstances), new { clusterId = dto.ClusterId }, instance);
+ }
+ catch (InvalidOperationException ex)
+ {
+ return BadRequest(new { message = ex.Message });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error adding instance to cluster {ClusterId}", dto.ClusterId);
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 移除服务实例
+ ///
+ /// 实例ID
+ /// 无内容响应
+ /// 成功移除实例
+ /// 实例不存在
+ /// 服务器内部错误
+ [HttpDelete("instances/{instanceId}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task RemoveInstance(long instanceId)
+ {
+ try
+ {
+ var result = await gatewayService.RemoveInstanceAsync(instanceId);
+ if (!result)
+ {
+ return NotFound(new { message = $"Instance {instanceId} not found" });
+ }
+ return Ok(new { message = "Instance removed successfully" });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error removing instance {InstanceId}", instanceId);
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 更新实例权重
+ /// 用于负载均衡策略中调整实例的请求分发权重
+ ///
+ /// 实例ID
+ /// 包含新权重值的请求体
+ /// 无内容响应
+ /// 成功更新权重
+ /// 实例不存在
+ /// 服务器内部错误
+ [HttpPut("instances/{instanceId}/weight")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task UpdateWeight(long instanceId, [FromBody] GatewayUpdateWeightDto dto)
+ {
+ try
+ {
+ var result = await gatewayService.UpdateInstanceWeightAsync(instanceId, dto.Weight);
+ if (!result)
+ {
+ return NotFound(new { message = $"Instance {instanceId} not found" });
+ }
+ return Ok(new { message = "Weight updated successfully" });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error updating weight for instance {InstanceId}", instanceId);
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+
+ ///
+ /// 重新加载网关配置
+ /// 触发网关重新加载所有配置,包括路由、服务、集群等配置
+ ///
+ /// 无内容响应
+ /// 成功重新加载配置
+ /// 服务器内部错误
+ [HttpPost("reload")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task ReloadGateway()
+ {
+ try
+ {
+ await gatewayService.ReloadGatewayAsync();
+ return Ok(new { message = "Gateway configuration reloaded successfully" });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error reloading gateway configuration");
+ return StatusCode(500, new { message = ex.Message });
+ }
+ }
+}
diff --git a/Controllers/GlobalUsing.cs b/Controllers/GlobalUsing.cs
new file mode 100644
index 0000000..737ce24
--- /dev/null
+++ b/Controllers/GlobalUsing.cs
@@ -0,0 +1,5 @@
+global using OpenIddict.Validation.AspNetCore;
+global using Fengling.Console.Models.Dtos;
+global using Fengling.Console.Services;
+global using Microsoft.AspNetCore.Authorization;
+global using Microsoft.AspNetCore.Mvc;
\ No newline at end of file
diff --git a/Controllers/OAuthClientsController.cs b/Controllers/OAuthClientsController.cs
index 09a7fc3..1a33f4a 100644
--- a/Controllers/OAuthClientsController.cs
+++ b/Controllers/OAuthClientsController.cs
@@ -1,12 +1,11 @@
-using Fengling.Console.Services;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-
namespace Fengling.Console.Controllers;
+///
+/// OAuth客户端管理控制器
+///
[ApiController]
-[Route("api/[controller]")]
-[Authorize]
+[Route("api/console/[controller]")]
+[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
public class OAuthClientsController : ControllerBase
{
private readonly IOAuthClientService _service;
@@ -20,25 +19,31 @@ public class OAuthClientsController : ControllerBase
_logger = logger;
}
+ ///
+ /// 获取OAuth客户端列表
+ ///
+ /// 分页查询参数,支持按显示名称、客户端ID和状态筛选
+ /// 分页的OAuth客户端列表,包含总数量、分页信息和客户端详情
+ /// 成功返回OAuth客户端分页列表
+ /// 服务器内部错误
[HttpGet]
- public async Task> GetClients(
- [FromQuery] int page = 1,
- [FromQuery] int pageSize = 10,
- [FromQuery] string? displayName = null,
- [FromQuery] string? clientId = null,
- [FromQuery] string? status = null)
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(OAuthClientListDto), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
+ public async Task> GetClients([FromQuery] OAuthClientQueryDto query)
{
try
{
- var (items, totalCount) = await _service.GetClientsAsync(page, pageSize, displayName, clientId, status);
+ var (items, totalCount) = await _service.GetClientsAsync(query.Page, query.PageSize, query.DisplayName, query.ClientId, query.Status);
- return Ok(new
+ var result = new OAuthClientListDto
{
- items,
- totalCount,
- page,
- pageSize
- });
+ Items = items.ToList(),
+ TotalCount = totalCount,
+ Page = query.Page,
+ PageSize = query.PageSize
+ };
+ return Ok(result);
}
catch (Exception ex)
{
@@ -47,14 +52,33 @@ public class OAuthClientsController : ControllerBase
}
}
+ ///
+ /// 获取OAuth客户端选项
+ ///
+ /// 包含客户端类型、授权类型、授权范围等可选值的配置选项
+ /// 成功返回客户端配置选项
[HttpGet("options")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
public ActionResult