192 lines
7.9 KiB
C#
192 lines
7.9 KiB
C#
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
|
using AMREZ.EOP.Abstractions.Applications.UseCases.Tenancy;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.AddBaseDomain;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.CreateTenant;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.ListDomains;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.ListTenants;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.MapDomain;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.RemoveBaseDomain;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.UnmapDomain;
|
|
using AMREZ.EOP.Contracts.DTOs.Tenancy.UpdateTenant;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace AMREZ.EOP.API.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public sealed class TenancyController : ControllerBase
|
|
{
|
|
private readonly ICreateTenantUseCase _createTenant;
|
|
private readonly IUpdateTenantUseCase _updateTenant;
|
|
private readonly IDeleteTenantUseCase _deleteTenant;
|
|
private readonly IMapDomainUseCase _mapDomain;
|
|
private readonly IUnmapDomainUseCase _unmapDomain;
|
|
private readonly IAddBaseDomainUseCase _addBaseDomain;
|
|
private readonly IRemoveBaseDomainUseCase _removeBaseDomain;
|
|
private readonly IListTenantsUseCase _listTenants;
|
|
private readonly IListDomainsUseCase _listDomains;
|
|
private readonly ITenantResolver _resolver;
|
|
|
|
public TenancyController(
|
|
ICreateTenantUseCase createTenant,
|
|
IUpdateTenantUseCase updateTenant,
|
|
IDeleteTenantUseCase deleteTenant,
|
|
IMapDomainUseCase mapDomain,
|
|
IUnmapDomainUseCase unmapDomain,
|
|
IAddBaseDomainUseCase addBaseDomain,
|
|
IRemoveBaseDomainUseCase removeBaseDomain,
|
|
IListTenantsUseCase listTenants,
|
|
IListDomainsUseCase listDomains,
|
|
ITenantResolver resolver)
|
|
{
|
|
_createTenant = createTenant;
|
|
_updateTenant = updateTenant;
|
|
_deleteTenant = deleteTenant;
|
|
_mapDomain = mapDomain;
|
|
_unmapDomain = unmapDomain;
|
|
_addBaseDomain = addBaseDomain;
|
|
_removeBaseDomain = removeBaseDomain;
|
|
_listTenants = listTenants;
|
|
_listDomains = listDomains;
|
|
_resolver = resolver;
|
|
}
|
|
|
|
private static object ErrTenantResolve => new
|
|
{
|
|
code = "TENANT_CONTEXT_NOT_RESOLVED",
|
|
message = "Check X-Tenant (e.g., public) or permission scope."
|
|
};
|
|
|
|
// Tenants
|
|
// Create: อย่า feed body เข้า resolver (tenant ยังไม่เกิด) ให้ใช้ context ปัจจุบันเท่านั้น
|
|
[HttpPost("tenants")]
|
|
public async Task<IActionResult> CreateTenant([FromBody] CreateTenantRequest body, CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _createTenant.ExecuteAsync(body, ct);
|
|
if (res is null)
|
|
return Conflict(new { code = "TENANT_EXISTS", message = "Use PUT to update/reactivate." });
|
|
|
|
return Created($"/api/Tenancy/tenants/{res.TenantKey}", res);
|
|
}
|
|
|
|
[HttpPut("tenants/{tenantKey}")]
|
|
public async Task<IActionResult> UpdateTenant([FromRoute] string tenantKey, [FromBody] UpdateTenantRequest body, CancellationToken ct)
|
|
{
|
|
if (!string.Equals(tenantKey?.Trim(), body.TenantKey?.Trim(), StringComparison.OrdinalIgnoreCase))
|
|
return BadRequest(new { code = "TENANT_KEY_MISMATCH", message = "Route tenantKey must match body.TenantKey" });
|
|
|
|
// การแก้ไข tenant ที่มีอยู่: ให้ resolver ใช้ tenantKey เป็น hint เพื่อบังคับ context ให้ถูกต้อง
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _updateTenant.ExecuteAsync(body, ct);
|
|
if (res is null)
|
|
return NotFound(new { code = "TENANT_UPDATE_FAILED", message = "Update failed (not found, precondition, or tenant not resolved)" });
|
|
|
|
return Ok(res);
|
|
}
|
|
|
|
[HttpDelete("tenants/{tenantKey}")]
|
|
public async Task<IActionResult> DeleteTenant([FromRoute] string tenantKey, CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var ok = await _deleteTenant.ExecuteAsync(tenantKey, ct);
|
|
if (!ok)
|
|
return NotFound(new { code = "TENANT_DELETE_FAILED", message = "Delete failed (not found or tenant not resolved)" });
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpGet("tenants")]
|
|
public async Task<IActionResult> ListTenants(CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _listTenants.ExecuteAsync(new ListTenantsRequest(), ct);
|
|
if (res is null)
|
|
return NotFound(new { code = "TENANT_LIST_FAILED", message = "Tenant resolve failed" });
|
|
|
|
return Ok(res);
|
|
}
|
|
|
|
// Domains
|
|
[HttpGet("domains")]
|
|
public async Task<IActionResult> ListDomains([FromQuery] string? tenantKey, CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _listDomains.ExecuteAsync(new ListDomainsRequest { TenantKey = tenantKey }, ct);
|
|
if (res is null)
|
|
return NotFound(new { code = "DOMAIN_LIST_FAILED", message = "Tenant resolve failed" });
|
|
|
|
return Ok(res);
|
|
}
|
|
|
|
[HttpPost("domains/map")]
|
|
public async Task<IActionResult> MapDomain([FromBody] MapDomainRequest body, CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: body?.TenantKey);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _mapDomain.ExecuteAsync(body, ct);
|
|
if (res is null)
|
|
return Conflict(new { code = "DOMAIN_MAP_FAILED", message = "Map failed (tenant/domain invalid or tenant not resolved)" });
|
|
|
|
return Ok(res);
|
|
}
|
|
|
|
[HttpDelete("domains/{domain}")]
|
|
public async Task<IActionResult> UnmapDomain([FromRoute] string domain, [FromQuery] string? tenantKey, CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _unmapDomain.ExecuteAsync(new UnmapDomainRequest { Domain = domain, TenantKey = tenantKey }, ct);
|
|
if (res is null)
|
|
return NotFound(new { code = "DOMAIN_UNMAP_FAILED", message = "Unmap failed (not found or tenant not resolved)" });
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPost("domains/base")]
|
|
public async Task<IActionResult> AddBaseDomain([FromBody] AddBaseDomainRequest body, CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _addBaseDomain.ExecuteAsync(body, ct);
|
|
if (res is null)
|
|
return Conflict(new { code = "BASE_DOMAIN_ADD_FAILED", message = "Add base domain failed (tenant not resolved or duplicate)" });
|
|
|
|
return Ok(res);
|
|
}
|
|
|
|
[HttpDelete("domains/base/{baseDomain}")]
|
|
public async Task<IActionResult> RemoveBaseDomain([FromRoute] string baseDomain, [FromQuery] string? tenantKey, CancellationToken ct)
|
|
{
|
|
var resolved = _resolver.Resolve(HttpContext, hint: null);
|
|
if (resolved is null)
|
|
return NotFound(ErrTenantResolve);
|
|
|
|
var res = await _removeBaseDomain.ExecuteAsync(new RemoveBaseDomainRequest { BaseDomain = baseDomain, TenantKey = tenantKey }, ct);
|
|
if (res is null)
|
|
return NotFound(new { code = "BASE_DOMAIN_REMOVE_FAILED", message = "Remove base domain failed (not found or tenant not resolved)" });
|
|
|
|
return NoContent();
|
|
}
|
|
} |