98 lines
3.8 KiB
C#
98 lines
3.8 KiB
C#
using System.Text.RegularExpressions;
|
|
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
|
using AMREZ.EOP.Contracts.DTOs.Authentications.Login;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace AMREZ.EOP.Infrastructures.Tenancy;
|
|
|
|
public sealed class DefaultTenantResolver : ITenantResolver
|
|
{
|
|
private readonly TenantMap _map;
|
|
private readonly ILogger<DefaultTenantResolver>? _log;
|
|
|
|
// แพลตฟอร์มสลัก (ปรับตามระบบจริงได้) — ใช้ "public" เป็นค่าเริ่ม
|
|
private const string PlatformSlug = "public";
|
|
|
|
public DefaultTenantResolver(
|
|
TenantMap map,
|
|
ILogger<DefaultTenantResolver>? log = null
|
|
)
|
|
{
|
|
_map = map;
|
|
_log = log;
|
|
}
|
|
|
|
public ITenantContext? Resolve(HttpContext http, object? hint = null)
|
|
{
|
|
var cid = http.TraceIdentifier;
|
|
var path = http.Request.Path.Value ?? string.Empty;
|
|
|
|
// ✅ ทางลัด: ขอ platform ตรง ๆ ด้วย hint
|
|
if (hint is string hs && string.Equals(hs, "@platform", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (_map.TryGetBySlug(PlatformSlug, out var platform))
|
|
{
|
|
_log?.LogInformation("Resolved by hint=@platform -> {Slug} cid={Cid}", PlatformSlug, cid);
|
|
return platform;
|
|
}
|
|
_log?.LogWarning("Platform slug '{Slug}' not found in TenantMap cid={Cid}", PlatformSlug, cid);
|
|
return null;
|
|
}
|
|
|
|
if (path.StartsWith("/api/Tenancy", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (_map.TryGetBySlug(PlatformSlug, out var platform))
|
|
{
|
|
// เก็บ target tenant แยกไว้ให้ repo/usecase ใช้
|
|
var headerTenant = http.Request.Headers["X-Tenant"].ToString();
|
|
if (!string.IsNullOrWhiteSpace(headerTenant))
|
|
http.Items["TargetTenantKey"] = headerTenant.Trim().ToLowerInvariant();
|
|
|
|
_log?.LogInformation("Resolved CONTROL-PLANE -> platform:{Slug} (path={Path}) cid={Cid}", PlatformSlug, path, cid);
|
|
return platform;
|
|
}
|
|
_log?.LogWarning("CONTROL-PLANE path but platform slug '{Slug}' missing cid={Cid}", PlatformSlug, cid);
|
|
return null;
|
|
}
|
|
|
|
var header = http.Request.Headers["X-Tenant"].ToString();
|
|
if (!string.IsNullOrWhiteSpace(header) && _map.TryGetBySlug(header, out var byHeader))
|
|
{
|
|
_log?.LogInformation("Resolved by header: {Tenant} cid={Cid}", header, cid);
|
|
return byHeader;
|
|
}
|
|
|
|
var host = http.Request.Host.Host;
|
|
if (_map.TryGetByDomain(host, out var byDomain))
|
|
{
|
|
_log?.LogInformation("Resolved by domain: {Host} -> {TenantId} cid={Cid}", host, byDomain.Id, cid);
|
|
return byDomain;
|
|
}
|
|
|
|
var sub = GetSubdomain(host);
|
|
if (sub != null && _map.TryGetBySlug(sub, out var bySub))
|
|
{
|
|
_log?.LogInformation("Resolved by subdomain: {Sub} cid={Cid}", sub, cid);
|
|
return bySub;
|
|
}
|
|
|
|
if (hint is LoginRequest body && !string.IsNullOrWhiteSpace(body.Tenant) &&
|
|
_map.TryGetBySlug(body.Tenant!, out var byBody))
|
|
{
|
|
_log?.LogInformation("Resolved by body: {Tenant} cid={Cid}", body.Tenant, cid);
|
|
return byBody;
|
|
}
|
|
|
|
_log?.LogWarning("Resolve FAILED host={Host} header={Header} cid={Cid}",
|
|
host, string.IsNullOrWhiteSpace(header) ? "<empty>" : header, cid);
|
|
return null;
|
|
}
|
|
|
|
private static string? GetSubdomain(string host)
|
|
{
|
|
if (Regex.IsMatch(host, @"^\d{1,3}(\.\d{1,3}){3}$")) return null; // IP
|
|
var parts = host.Split('.', StringSplitOptions.RemoveEmptyEntries);
|
|
return parts.Length >= 3 ? parts[0] : null;
|
|
}
|
|
} |