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 {