129 lines
4.8 KiB
C#
129 lines
4.8 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Tiku.Api.Contracts;
|
|
using Tiku.Application.Security;
|
|
using Tiku.Application.Tenancy;
|
|
using Tiku.Domain.Tenancy;
|
|
|
|
namespace Tiku.Api.Controllers;
|
|
|
|
[ApiController]
|
|
[Tags("租户端-公开配置")]
|
|
[AllowAnonymous]
|
|
[Produces("application/json")]
|
|
[Route("api/public/tenant")]
|
|
public sealed class TenantPublicController(
|
|
ITenantContext tenantContext,
|
|
ITenantContextInitializer tenantContextInitializer,
|
|
ITenantDirectory tenantDirectory,
|
|
IPublicTenantConfigurationQuery publicConfiguration) : ControllerBase
|
|
{
|
|
[HttpGet("resolve")]
|
|
[EndpointSummary("解析当前租户")]
|
|
[EndpointDescription("根据访问域名、host 参数、x-tenant-code 或 tenantCode 查询启用中的租户及公开品牌配置。")]
|
|
[ProducesResponseType<TenantResolveResponseDto>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult<TenantResolveResponseDto>> Resolve(
|
|
[FromQuery] TenantResolveQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var tenantCode = NormalizeTenantCode(query.TenantCode ?? Request.Headers["x-tenant-code"].FirstOrDefault());
|
|
var host = NormalizeHost(query.Host ?? Request.Host.Host);
|
|
|
|
var directoryEntry = tenantContext.ResolutionSource == TenantResolutionSource.Host
|
|
? await tenantDirectory.FindByHostAsync(Request.Host.Host, cancellationToken)
|
|
: !string.IsNullOrWhiteSpace(tenantCode)
|
|
? await tenantDirectory.FindByCodeAsync(tenantCode, cancellationToken)
|
|
: string.IsNullOrWhiteSpace(host)
|
|
? null
|
|
: await tenantDirectory.FindByHostAsync(host, cancellationToken);
|
|
|
|
var tenant = directoryEntry is null
|
|
? null
|
|
: new TenantLookupResult(
|
|
directoryEntry.TenantId,
|
|
directoryEntry.TenantCode,
|
|
directoryEntry.Name,
|
|
directoryEntry.Status,
|
|
directoryEntry.Mode,
|
|
directoryEntry.Host);
|
|
|
|
if (tenant is null)
|
|
return NotFound(new ProblemDetails
|
|
{
|
|
Title = "Tenant was not found.",
|
|
Status = StatusCodes.Status404NotFound,
|
|
Detail = "No active tenant matches the supplied host or tenantCode."
|
|
});
|
|
|
|
tenantContextInitializer.Initialize(
|
|
tenant.Id,
|
|
tenant.Slug,
|
|
tenant.Host is null ? TenantResolutionSource.TenantCode : TenantResolutionSource.Host);
|
|
|
|
return Ok(await BuildResponseAsync(tenant, tenant.Host, cancellationToken));
|
|
}
|
|
|
|
[HttpGet("current-public")]
|
|
[EndpointSummary("获取公开租户配置")]
|
|
[EndpointDescription("公开页面使用的租户品牌、功能开关和 public config。和 resolve 返回结构一致。")]
|
|
[ProducesResponseType<TenantResolveResponseDto>(StatusCodes.Status200OK)]
|
|
[ProducesResponseType<ProblemDetails>(StatusCodes.Status404NotFound)]
|
|
public Task<ActionResult<TenantResolveResponseDto>> CurrentPublic(
|
|
[FromQuery] TenantResolveQueryDto query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Resolve(query, cancellationToken);
|
|
}
|
|
|
|
private async Task<TenantResolveResponseDto> BuildResponseAsync(
|
|
TenantLookupResult tenant,
|
|
string? host,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var configuration = await publicConfiguration.GetAsync(tenant.Id, cancellationToken);
|
|
|
|
return new TenantResolveResponseDto(
|
|
new PublicTenantDto(
|
|
tenant.Id,
|
|
tenant.Slug,
|
|
tenant.Name,
|
|
tenant.Status,
|
|
tenant.Mode,
|
|
host),
|
|
new PublicTenantBrandingDto(
|
|
configuration.BrandName,
|
|
configuration.ShortName,
|
|
configuration.Slogan,
|
|
configuration.LogoUrl,
|
|
configuration.FaviconUrl,
|
|
configuration.ServiceWechat,
|
|
configuration.ServiceAccountName,
|
|
configuration.Theme,
|
|
configuration.PublicAssets),
|
|
configuration.FeatureFlags,
|
|
configuration.AdminFeatureFlags,
|
|
configuration.PublicConfig);
|
|
}
|
|
|
|
private static string? NormalizeTenantCode(string? value)
|
|
{
|
|
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
|
}
|
|
|
|
private static string? NormalizeHost(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value)) return null;
|
|
|
|
var host = value.Trim().ToLowerInvariant();
|
|
return host.Split(':', 2)[0];
|
|
}
|
|
|
|
private sealed record TenantLookupResult(
|
|
Guid Id,
|
|
string Slug,
|
|
string Name,
|
|
TenantStatus Status,
|
|
TenantMode Mode,
|
|
string? Host);
|
|
} |