diff --git a/build/code.quality.props b/build/code.quality.props index 4ceae2fd..da9595a1 100644 --- a/build/code.quality.props +++ b/build/code.quality.props @@ -23,7 +23,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/scripts/switcher.furion.json b/scripts/switcher.furion.json index 920f92cb..f149da53 100644 --- a/scripts/switcher.furion.json +++ b/scripts/switcher.furion.json @@ -9,7 +9,7 @@ "packages": [ { "packageName": "Furion.Pure.NS", - "version": "4.9.3-ns1" + "version": "4.9.4-ns1" } ] } diff --git a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClient.cs b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClient.cs index 3f3687e4..1a8cb3c4 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClient.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Dependency/Fields/IFieldCreatedClient.cs @@ -10,11 +10,6 @@ public interface IFieldCreatedClient /// int? CreatedClientIp { get; init; } - /// - /// 创建者来源地址 - /// - string CreatedReferer { get; init; } - /// /// 创建者客户端用户代理 /// diff --git a/src/backend/NetAdmin.Domain/DbMaps/Dependency/SimpleEntity.cs b/src/backend/NetAdmin.Domain/DbMaps/Dependency/SimpleEntity.cs index b3269621..8d4eab00 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Dependency/SimpleEntity.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Dependency/SimpleEntity.cs @@ -1,5 +1,16 @@ +using NetAdmin.Domain.Attributes; + namespace NetAdmin.Domain.DbMaps.Dependency; +/// +public abstract record SimpleEntity : SimpleEntity +{ + /// + [Column(IsIdentity = false, IsPrimary = true, Position = 1)] + [Snowflake] + public override long Id { get; init; } +} + /// /// 简单实体 /// diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs index 835abc87..d01968af 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_JobRecord.cs @@ -31,7 +31,7 @@ public record Sys_JobRecord : LiteImmutableEntity /// [Column] [JsonIgnore] - public virtual int HttpStatusCode { get; init; } + public int HttpStatusCode { get; init; } /// /// 拥有者信息 diff --git a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs index 6416cae8..ddef57fe 100644 --- a/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs +++ b/src/backend/NetAdmin.Domain/DbMaps/Sys/Sys_RequestLog.cs @@ -8,9 +8,10 @@ namespace NetAdmin.Domain.DbMaps.Sys; /// [Index(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiId), nameof(ApiId), false)] [Index(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)] +[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(UserId), nameof(UserId), false)] [Index(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)] [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))] -public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient +public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldCreatedClient { /// /// 接口 @@ -26,23 +27,17 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient [JsonIgnore] public virtual string ApiId { get; init; } - /// - /// 创建者客户端IP - /// + /// [Column(Position = -1)] [JsonIgnore] public int? CreatedClientIp { get; init; } - /// - /// 创建者来源地址 - /// - [Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] + /// + [Column(ServerTime = DateTimeKind.Local, CanUpdate = false, Position = -1)] [JsonIgnore] - public string CreatedReferer { get; init; } + public virtual DateTime CreatedTime { get; init; } - /// - /// 创建者客户端用户代理 - /// + /// #if DBTYPE_SQLSERVER [Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)] #else @@ -76,17 +71,6 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient [JsonIgnore] public virtual string Exception { get; init; } - /// - /// 附加数据 - /// - #if DBTYPE_SQLSERVER - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)] - #else - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] - #endif - [JsonIgnore] - public virtual string ExtraData { get; init; } - /// /// HTTP状态码 /// @@ -101,13 +85,6 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient [JsonIgnore] public virtual string Method { get; init; } - /// - /// 来源地址 - /// - [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)] - [JsonIgnore] - public virtual string ReferUrl { get; init; } - /// /// 请求内容 /// @@ -179,4 +156,18 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient [Column] [JsonIgnore] public virtual int? ServerIp { get; init; } + + /// + /// 用户 + /// + [JsonIgnore] + [Navigate(nameof(UserId))] + public Sys_User User { get; init; } + + /// + /// 用户编号 + /// + [Column] + [JsonIgnore] + public virtual long? UserId { get; init; } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs index 98b3fd5d..bb2d296d 100644 --- a/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs +++ b/src/backend/NetAdmin.Domain/Dto/Sys/RequestLog/QueryRequestLogRsp.cs @@ -1,5 +1,6 @@ using NetAdmin.Domain.DbMaps.Dependency.Fields; using NetAdmin.Domain.DbMaps.Sys; +using NetAdmin.Domain.Dto.Sys.User; namespace NetAdmin.Domain.Dto.Sys.RequestLog; @@ -13,6 +14,11 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister /// public new string CreatedClientIp => base.CreatedClientIp?.ToIpV4(); + /// + /// 登录名 + /// + public string LoginName => RequestBody?.ToObject()?.Account; + /// /// 操作系统 /// @@ -35,10 +41,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override string CreatedUserAgent { get; init; } - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string CreatedUserName { get; init; } - /// [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override long Duration { get; init; } @@ -51,10 +53,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override string Exception { get; init; } - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string ExtraData { get; init; } - /// [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override int HttpStatusCode { get; init; } @@ -63,10 +61,6 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override string Method { get; init; } - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public override string ReferUrl { get; init; } - /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override string RequestBody { get; init; } @@ -99,6 +93,13 @@ public sealed record QueryRequestLogRsp : Sys_RequestLog, IRegister [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public override int? ServerIp { get; init; } + /// + public new QueryUserRsp User { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public override long? UserId { get; init; } + /// public void Register(TypeAdapterConfig config) { diff --git a/src/backend/NetAdmin.Host/Utils/RequestLogger.cs b/src/backend/NetAdmin.Host/Utils/RequestLogger.cs index f17b7125..80ce721c 100644 --- a/src/backend/NetAdmin.Host/Utils/RequestLogger.cs +++ b/src/backend/NetAdmin.Host/Utils/RequestLogger.cs @@ -7,17 +7,10 @@ namespace NetAdmin.Host.Utils; /// /// 请求日志记录器 /// -public sealed class RequestLogger( - ILogger logger - , IOptions specificationDocumentSettingsOptions - , IEventPublisher eventPublisher) : ISingleton +public sealed class RequestLogger(ILogger logger, IEventPublisher eventPublisher) : ISingleton { private static readonly string[] _textContentTypes = ["text", "json", "xml", "urlencoded"]; - private readonly int _tokenPrefixLength - = specificationDocumentSettingsOptions?.Value.SecurityDefinitions?[0]?.Scheme?.Length + 1 ?? - 0; // eg. "Bearer "; - /// /// 生成审计数据 /// @@ -30,7 +23,6 @@ public sealed class RequestLogger( var auditData = new CreateRequestLogReq { Duration = duration , Method = context.Request.Method - , ReferUrl = context.Request.GetRefererUrlAddress() , RequestContentType = context.Request.ContentType , RequestBody = Array.Exists( // _textContentTypes @@ -50,8 +42,7 @@ public sealed class RequestLogger( , HttpStatusCode = context.Response.StatusCode , ErrorCode = errorCode , Exception = exception?.Error.ToString() - , CreatedUserId = associatedUser?.UserId - , CreatedUserName = associatedUser?.UserName + , UserId = associatedUser?.UserId , CreatedUserAgent = context.Request.Headers.UserAgent.ToString() , CreatedClientIp = context.GetRealIpAddress() ?.MapToIPv4() @@ -77,8 +68,9 @@ public sealed class RequestLogger( ContextUserToken userToken = null; try { - var jsonWebToken = JWTEncryption.ReadJwtToken(token[_tokenPrefixLength..]); - var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken)); + var jsonWebToken + = JWTEncryption.ReadJwtToken(token.TrimStart($"{Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA} ")); + var claim = jsonWebToken?.Claims.FirstOrDefault(y => y.Type == nameof(ContextUserToken)); userToken = claim?.Value.ToObject(); } catch (Exception ex) { diff --git a/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs b/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs index 87477013..5a049d5b 100644 --- a/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs +++ b/src/backend/NetAdmin.Host/Utils/SqlAuditor.cs @@ -63,13 +63,6 @@ public sealed class SqlAuditor : ISingleton } } - private static void SetCreatedReferer(AuditValueEventArgs e) - { - if (e.Value is null or "") { - e.Value = App.HttpContext?.Request.GetRefererUrlAddress(); - } - } - private static void SetCreatedTime(AuditValueEventArgs e) { if (e.Value == null || (e.Value is DateTime val && val == default)) { @@ -119,9 +112,6 @@ public sealed class SqlAuditor : ISingleton case nameof(IFieldCreatedClient.CreatedUserAgent): SetCreatedUserAgent(e); break; - case nameof(IFieldCreatedClient.CreatedReferer): - SetCreatedReferer(e); - break; default: return; } diff --git a/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs b/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs index c63044b7..0c20ed29 100644 --- a/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs +++ b/src/backend/NetAdmin.Infrastructure/Constant/Chars.cs @@ -67,6 +67,7 @@ public static class Chars public const string FLG_HTTP_METHOD_POST = "POST"; public const string FLG_HTTP_METHOD_PUT = "PUT"; public const string FLG_HTTP_METHOD_TRACE = "TRACE"; + public const string FLG_PATH_API_SYS_USER_LOGIN_BY_PWD = "api/sys/user/login.by.pwd"; public const string FLG_PATH_PREFIX_HEALTH_CHECK = "probe/health.check"; public const string FLG_RANDOM_UNAME_PWD = "VcXlp7WY"; public const string FLG_REDIS_INSTANCE_DATA_CACHE = "DataCache"; diff --git a/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj b/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj index 4fdb361d..0a3b98b2 100644 --- a/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj +++ b/src/backend/NetAdmin.Infrastructure/NetAdmin.Infrastructure.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs index 65875f52..0b21470c 100644 --- a/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs +++ b/src/backend/NetAdmin.SysComponent.Application/Services/Sys/RequestLogService.cs @@ -12,6 +12,8 @@ namespace NetAdmin.SysComponent.Application.Services.Sys; public sealed class RequestLogService(BasicRepository rpo) // : RepositoryService(rpo), IRequestLogService { + private static readonly Regex _regex = new(Chars.RGXL_IP_V4); + /// public async Task BulkDeleteAsync(BulkReq req) { @@ -134,26 +136,45 @@ public sealed class RequestLogService(BasicRepository rpo) public async Task> PagedQueryAsync(PagedQueryReq req) { req.ThrowIfInvalid(); - var list = await QueryInternal(req) - .Page(req.Page, req.PageSize) - #if DBTYPE_SQLSERVER - .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) - #endif - .Count(out var total) - .ToListAsync(a => new { - a.ApiId - , ApiSummary = a.Api.Summary - , a.ExtraData - , a.CreatedClientIp - , a.CreatedTime - , a.CreatedUserName - , a.Duration - , a.Method - , a.CreatedUserAgent - , a.HttpStatusCode - , a.Id - }) - .ConfigureAwait(false); + var select = QueryInternal(req) + .Page(req.Page, req.PageSize) + #if DBTYPE_SQLSERVER + .WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait) + #endif + .Count(out var total); + object list = req.DynamicFilter?.Filters?.Exists( + x => nameof(QueryRequestLogReq.ApiId).Equals(x.Field, StringComparison.OrdinalIgnoreCase) && + Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD.Equals( // + x.Value.ToString(), StringComparison.OrdinalIgnoreCase)) ?? false + ? await select.ToListAsync(a => new { + a.ApiId + , ApiSummary = a.Api.Summary + , a.CreatedClientIp + , a.CreatedTime + , a.Duration + , a.Method + , a.CreatedUserAgent + , a.HttpStatusCode + , a.Id + , a.UserId + , a.User + , a.RequestBody + }) + .ConfigureAwait(false) + : await select.ToListAsync(a => new { + a.ApiId + , ApiSummary = a.Api.Summary + , a.CreatedClientIp + , a.CreatedTime + , a.Duration + , a.Method + , a.CreatedUserAgent + , a.HttpStatusCode + , a.Id + , a.UserId + , a.User + }) + .ConfigureAwait(false); return new PagedQueryRsp(req.Page, req.PageSize, total , list.Adapt>()); @@ -175,7 +196,18 @@ public sealed class RequestLogService(BasicRepository rpo) private ISelect QueryInternal(QueryReq req) { - var ret = Rpo.Select.Include(a => a.Api).WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); + var ret = Rpo.Select.Include(a => a.Api).Include(a => a.User).WhereDynamicFilter(req.DynamicFilter); + if (req.Filter?.Id is not 0) { + ret = ret.WhereDynamic(req.Filter); + } + + if (req.Keywords?.Length > 0) { + ret = _regex.IsMatch(req.Keywords) + ? ret.Where(a => a.CreatedClientIp == req.Keywords.IpV4ToInt32()) + : ret.Where(a => a.Id == req.Keywords.Int64Try(0) || a.UserId == req.Keywords.Int64Try(0) || + a.User.UserName == req.Keywords || a.RequestBody.Contains(req.Keywords)); + } + switch (req.Order) { case Orders.None: return ret; diff --git a/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs b/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs index d65abbe9..69f2c1cf 100644 --- a/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs +++ b/src/backend/NetAdmin.SysComponent.Host/Subscribers/OperationLogger.cs @@ -1,5 +1,3 @@ -using NetAdmin.Domain.Dto.Sys.RequestLog; -using NetAdmin.Domain.Dto.Sys.User; using NetAdmin.Domain.Events.Sys; using NetAdmin.SysComponent.Application.Services.Sys.Dependency; @@ -27,22 +25,7 @@ public sealed class OperationLogger : IEventSubscriber return; } - CreateRequestLogReq logReq = null; - - // 登录日志特殊处理 - if (operationEvent.Data.ApiId.Equals("api/sys/user/login.by.pwd", StringComparison.OrdinalIgnoreCase)) { - try { - var loginReq = operationEvent.Data.RequestBody.ToObject(); - logReq = operationEvent.Data with { ExtraData = loginReq.Account }; - } - catch { - // ignored - } - } - - logReq ??= operationEvent.Data; - var logService = App.GetService(); - logReq.TruncateStrings(); - _ = await logService.CreateAsync(logReq).ConfigureAwait(false); + operationEvent.Data.TruncateStrings(); + _ = await App.GetService().CreateAsync(operationEvent.Data).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/backend/NetAdmin.Tests/WebApiTestBase.cs b/src/backend/NetAdmin.Tests/WebApiTestBase.cs index ca4e588c..09e6edc0 100644 --- a/src/backend/NetAdmin.Tests/WebApiTestBase.cs +++ b/src/backend/NetAdmin.Tests/WebApiTestBase.cs @@ -15,10 +15,8 @@ public abstract class WebApiTestBase(WebApplicationFactory factory, ITestO : IClassFixture> where T : AppStartup { - private const string _ACCOUNT = "root"; - private const string _API_SYS_USER_LOGIN_BY_PWD = "/api/sys/user/login.by.pwd"; - private const string _AUTH_SCHEMA = "Bearer"; - private const string _PASSWORD = "1234qwer"; + private const string _ACCOUNT = "root"; + private const string _PASSWORD = "1234qwer"; private string _accessToken; /// @@ -28,7 +26,7 @@ public abstract class WebApiTestBase(WebApplicationFactory factory, ITestO { var client = factory.CreateClient(); if (_accessToken == null) { - var loginRsp = await client.PostAsync(_API_SYS_USER_LOGIN_BY_PWD + var loginRsp = await client.PostAsync(Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD , JsonContent.Create( new LoginByPwdReq { Password = _PASSWORD, Account = _ACCOUNT })) .ConfigureAwait(false); @@ -37,7 +35,8 @@ public abstract class WebApiTestBase(WebApplicationFactory factory, ITestO _accessToken = loginRspObj.Data.AccessToken; } - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(_AUTH_SCHEMA, _accessToken); + client.DefaultRequestHeaders.Authorization + = new AuthenticationHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA, _accessToken); var ret = await client.PostAsync(url, content).ConfigureAwait(false); testOutputHelper.WriteLine(await ret.Content.ReadAsStringAsync().ConfigureAwait(false)); return ret; diff --git a/src/frontend/admin/src/components/scSelectFilter/index.vue b/src/frontend/admin/src/components/scSelectFilter/index.vue index 5be3e561..612594d0 100644 --- a/src/frontend/admin/src/components/scSelectFilter/index.vue +++ b/src/frontend/admin/src/components/scSelectFilter/index.vue @@ -130,11 +130,14 @@ export default {