using Fengling.AuthService.Models; using Fengling.Console.Models.Dtos; using Fengling.Console.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Security.Claims; namespace Fengling.Console.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] public class OAuthClientsController : ControllerBase { private readonly IOAuthClientService _service; private readonly ILogger _logger; public OAuthClientsController( IOAuthClientService service, ILogger logger) { _service = service; _logger = logger; } [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) { try { var (items, totalCount) = await _service.GetClientsAsync(page, pageSize, displayName, clientId, status); var result = items.Select(c => new OAuthClientDto { Id = c.Id, ClientId = c.ClientId, DisplayName = c.DisplayName, RedirectUris = c.RedirectUris, PostLogoutRedirectUris = c.PostLogoutRedirectUris, Scopes = c.Scopes, GrantTypes = c.GrantTypes, ClientType = c.ClientType, ConsentType = c.ConsentType, Status = c.Status, Description = c.Description, CreatedAt = c.CreatedAt, UpdatedAt = c.UpdatedAt, }); return Ok(new { items = result, totalCount, page, pageSize }); } catch (Exception ex) { _logger.LogError(ex, "Error getting clients"); return StatusCode(500, new { message = ex.Message }); } } [HttpGet("options")] public ActionResult GetClientOptions() { return Ok(_service.GetClientOptions()); } [HttpGet("{id}")] public async Task> GetClient(long id) { try { var client = await _service.GetClientAsync(id); if (client == null) { return NotFound(); } return Ok(new OAuthClientDto { Id = client.Id, ClientId = client.ClientId, DisplayName = client.DisplayName, RedirectUris = client.RedirectUris, PostLogoutRedirectUris = client.PostLogoutRedirectUris, Scopes = client.Scopes, GrantTypes = client.GrantTypes, ClientType = client.ClientType, ConsentType = client.ConsentType, Status = client.Status, Description = client.Description, CreatedAt = client.CreatedAt, UpdatedAt = client.UpdatedAt, }); } catch (Exception ex) { _logger.LogError(ex, "Error getting client {Id}", id); return StatusCode(500, new { message = ex.Message }); } } [HttpPost] public async Task> CreateClient([FromBody] CreateOAuthClientDto dto) { try { var client = new OAuthApplication { ClientId = dto.ClientId, ClientSecret = string.IsNullOrEmpty(dto.ClientSecret) ? GenerateSecureSecret() : dto.ClientSecret, DisplayName = dto.DisplayName, RedirectUris = dto.RedirectUris ?? Array.Empty(), PostLogoutRedirectUris = dto.PostLogoutRedirectUris ?? Array.Empty(), Scopes = dto.Scopes ?? new[] { "openid", "profile", "email", "api" }, GrantTypes = dto.GrantTypes ?? new[] { "authorization_code", "refresh_token" }, ClientType = dto.ClientType ?? "confidential", ConsentType = dto.ConsentType ?? "explicit", Status = dto.Status ?? "active", Description = dto.Description, CreatedAt = DateTime.UtcNow, }; var created = await _service.CreateClientAsync(client); return CreatedAtAction(nameof(GetClient), new { id = created.Id }, new OAuthClientDto { Id = created.Id, ClientId = created.ClientId, ClientSecret = created.ClientSecret, DisplayName = created.DisplayName, Status = created.Status, CreatedAt = created.CreatedAt, }); } catch (InvalidOperationException ex) { return BadRequest(new { message = ex.Message }); } catch (Exception ex) { _logger.LogError(ex, "Error creating client"); return StatusCode(500, new { message = ex.Message }); } } [HttpPost("{id}/generate-secret")] public async Task GenerateSecret(long id) { try { var client = await _service.GenerateSecretAsync(id); return Ok(new { clientId = client.ClientId, clientSecret = client.ClientSecret, message = "新密钥已生成,请妥善保管,刷新后将无法再次查看" }); } catch (KeyNotFoundException ex) { return NotFound(new { message = ex.Message }); } catch (Exception ex) { _logger.LogError(ex, "Error generating secret for client {Id}", id); return StatusCode(500, new { message = ex.Message }); } } [HttpPost("{id}/toggle-status")] public async Task ToggleStatus(long id) { try { var client = await _service.ToggleStatusAsync(id); return Ok(new { clientId = client.ClientId, newStatus = client.Status, message = $"客户端状态已更改为 {client.Status}" }); } catch (KeyNotFoundException ex) { return NotFound(new { message = ex.Message }); } catch (Exception ex) { _logger.LogError(ex, "Error toggling status for client {Id}", id); return StatusCode(500, new { message = ex.Message }); } } [HttpPut("{id}")] public async Task UpdateClient(long id, [FromBody] UpdateOAuthClientDto dto) { try { var client = await _service.GetClientAsync(id); if (client == null) { return NotFound(); } var updated = new OAuthApplication { Id = id, ClientId = client.ClientId, ClientSecret = client.ClientSecret, DisplayName = dto.DisplayName, RedirectUris = dto.RedirectUris, PostLogoutRedirectUris = dto.PostLogoutRedirectUris, Scopes = dto.Scopes, GrantTypes = dto.GrantTypes, ClientType = dto.ClientType, ConsentType = dto.ConsentType, Status = dto.Status, Description = dto.Description, CreatedAt = client.CreatedAt, }; await _service.UpdateClientAsync(id, updated); return NoContent(); } catch (KeyNotFoundException ex) { return NotFound(new { message = ex.Message }); } catch (Exception ex) { _logger.LogError(ex, "Error updating client {Id}", id); return StatusCode(500, new { message = ex.Message }); } } [HttpDelete("{id}")] public async Task DeleteClient(long id) { try { await _service.DeleteClientAsync(id); return NoContent(); } catch (KeyNotFoundException ex) { return NotFound(new { message = ex.Message }); } catch (Exception ex) { _logger.LogError(ex, "Error deleting client {Id}", id); return StatusCode(500, new { message = ex.Message }); } } private static string GenerateSecureSecret(int length = 32) { var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var bytes = System.Security.Cryptography.RandomNumberGenerator.GetBytes(length); return new string(bytes.Select(b => chars[b % chars.Length]).ToArray()); } }