using FlatRender.IdentitySvc.Application.Services.Interfaces; using FlatRender.IdentitySvc.Models.Requests; using FlatRender.IdentitySvc.Models.Responses; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace FlatRender.IdentitySvc.Controllers; [ApiController] [Route("v1/tenants")] [Authorize] public class TenantsController(ITenantService tenantService) : ControllerBase { [HttpGet] [ProducesResponseType(typeof(PagedResponse), 200)] public async Task List([FromQuery] int page = 1, [FromQuery] int pageSize = 20) => Ok(await tenantService.ListAsync(page, pageSize)); [HttpPost] [ProducesResponseType(typeof(TenantResponse), 201)] public async Task Create([FromBody] CreateTenantRequest request) { var result = await tenantService.CreateAsync(request); return StatusCode(201, result); } [HttpGet("by-slug/{slug}")] [AllowAnonymous] [ProducesResponseType(typeof(TenantResponse), 200)] public async Task GetBySlug(string slug) => Ok(await tenantService.GetBySlugAsync(slug)); [HttpGet("{tenantId:guid}")] [ProducesResponseType(typeof(TenantResponse), 200)] public async Task GetById(Guid tenantId) => Ok(await tenantService.GetByIdAsync(tenantId)); [HttpPatch("{tenantId:guid}")] [ProducesResponseType(typeof(TenantResponse), 200)] public async Task Update(Guid tenantId, [FromBody] UpdateTenantRequest request) => Ok(await tenantService.UpdateAsync(tenantId, request)); [HttpGet("{tenantId:guid}/branding")] [ProducesResponseType(typeof(TenantBrandingResponse), 200)] public async Task GetBranding(Guid tenantId) => Ok(await tenantService.GetBrandingAsync(tenantId)); [HttpPut("{tenantId:guid}/branding")] [ProducesResponseType(typeof(TenantBrandingResponse), 200)] public async Task UpsertBranding(Guid tenantId, [FromBody] TenantBrandingRequest request) => Ok(await tenantService.UpsertBrandingAsync(tenantId, request)); [HttpPost("{tenantId:guid}/domains/verify")] [ProducesResponseType(typeof(DomainVerificationResponse), 200)] public async Task VerifyDomain(Guid tenantId, [FromBody] StartDomainVerificationRequest request) => Ok(await tenantService.StartDomainVerificationAsync(tenantId, request.Domain, request.Method)); [HttpGet("{tenantId:guid}/usage")] [ProducesResponseType(typeof(object), 200)] public async Task GetUsage( Guid tenantId, [FromQuery] DateOnly from, [FromQuery] DateOnly to) { var data = await tenantService.GetUsageAsync(tenantId, from, to); return Ok(new { data }); } // ── API Keys ────────────────────────────────────────────────────────── [HttpGet("{tenantId:guid}/api-keys")] public async Task GetApiKeys(Guid tenantId) => Ok(new { data = await tenantService.GetApiKeysAsync(tenantId) }); [HttpPost("{tenantId:guid}/api-keys")] public async Task CreateApiKey(Guid tenantId, [FromBody] CreateApiKeyRequest request) { var userId = GetUserId(); var result = await tenantService.CreateApiKeyAsync(tenantId, userId, request); return StatusCode(201, result); } [HttpDelete("{tenantId:guid}/api-keys/{apiKeyId:guid}")] public async Task RevokeApiKey(Guid tenantId, Guid apiKeyId, [FromBody] RevokeApiKeyRequest? request) { await tenantService.RevokeApiKeyAsync(tenantId, apiKeyId, request?.Reason); return NoContent(); } // ── Webhooks ────────────────────────────────────────────────────────── [HttpGet("{tenantId:guid}/webhooks")] public async Task GetWebhooks(Guid tenantId) => Ok(new { data = await tenantService.GetWebhooksAsync(tenantId) }); [HttpPost("{tenantId:guid}/webhooks")] public async Task CreateWebhook(Guid tenantId, [FromBody] CreateWebhookRequest request) { var result = await tenantService.CreateWebhookAsync(tenantId, request); return StatusCode(201, result); } [HttpDelete("{tenantId:guid}/webhooks/{webhookId:guid}")] public async Task DeleteWebhook(Guid tenantId, Guid webhookId) { await tenantService.DeleteWebhookAsync(tenantId, webhookId); return NoContent(); } [HttpGet("{tenantId:guid}/webhooks/{webhookId:guid}/deliveries")] public async Task GetWebhookDeliveries(Guid tenantId, Guid webhookId) => Ok(new { data = await tenantService.GetWebhookDeliveriesAsync(tenantId, webhookId) }); private Guid GetUserId() => Guid.Parse(User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value ?? User.FindFirst("sub")?.Value ?? throw new UnauthorizedAccessException()); } [ApiController] [Route("v1/api-keys")] public class ApiKeyValidationController(ITenantService tenantService) : ControllerBase { [HttpPost("validate")] public async Task Validate([FromBody] ValidateApiKeyRequest request) => Ok(await tenantService.ValidateApiKeyAsync(request.KeyPrefix, request.KeyHash, request.IpAddress)); }