using Admin.NET.Core.Service;
using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
using SqlSugar;
namespace Admin.NET.Plugin.AiDOP.DataPlatform.Inbound;
/// grant 授权 + IP 白名单 + 限流。行数上限由接收服务按 grant/entity 取 min。
public sealed class MdpInboundAuthService : ITransient
{
private readonly ISqlSugarClient _db;
private readonly SysCacheService _cache;
public MdpInboundAuthService(ISqlSugarClient db, SysCacheService cache)
{
_db = db;
_cache = cache;
}
/// 返回 null = 通过;否则 (httpStatus, message)。
public async Task<(int Status, string Message)?> CheckAsync(
string accessKey, long tenantId, string entityCode, string clientIp, CancellationToken ct)
{
var code = (entityCode ?? string.Empty).Trim().ToUpperInvariant();
var entity = await _db.Queryable()
.Where(e => e.InboundEnabled == 1)
.Where("UPPER(entity_code) = @code", new SugarParameter("@code", code))
.FirstAsync(ct);
if (entity == null)
return (403, "entity not authorized");
var grant = await _db.Queryable()
.Where(g => g.AccessKey == accessKey && g.Status == 1)
.Where("UPPER(entity_code) = @code", new SugarParameter("@code", code))
.FirstAsync(ct);
if (grant == null)
return (403, "entity not authorized");
if (grant.TenantId != tenantId)
return (403, "entity not authorized");
if (!string.IsNullOrWhiteSpace(grant.IpAllowlist))
{
var allowed = grant.IpAllowlist
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var ip = (clientIp ?? string.Empty).Trim();
if (ip.Length == 0 || !allowed.Contains(ip, StringComparer.OrdinalIgnoreCase))
return (403, "ip not allowed");
}
var minute = DateTime.Now.ToString("yyyyMMddHHmm");
var rateKey = $"AIDOP_INBOUND_RATE:{accessKey}:{code}:{minute}";
var current = _cache.Get(rateKey);
var next = current + 1;
_cache.Set(rateKey, next, TimeSpan.FromMinutes(1));
if (next > grant.RateLimitPerMin)
return (429, "rate limit exceeded");
return null;
}
public async Task GetGrantAsync(string accessKey, string entityCode, CancellationToken ct)
{
var code = (entityCode ?? string.Empty).Trim().ToUpperInvariant();
return await _db.Queryable()
.Where(g => g.AccessKey == accessKey && g.Status == 1)
.Where("UPPER(entity_code) = @code", new SugarParameter("@code", code))
.FirstAsync(ct);
}
}