mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-22 03:24:29 +08:00
Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4a3b5163f1 | ||
![]() |
3a0946d357 | ||
![]() |
9dd21c644c | ||
![]() |
6713190a23 | ||
![]() |
9f24533425 | ||
![]() |
ed1d9374eb | ||
![]() |
f14d27129e | ||
![]() |
5b04f02fbe | ||
![]() |
227080e332 | ||
![]() |
1e87482a49 | ||
![]() |
054d31c3ea | ||
![]() |
3a068a7b03 | ||
![]() |
b12e923c99 | ||
![]() |
ab33eed8d3 | ||
![]() |
d930a9a8eb | ||
![]() |
af381fce12 | ||
![]() |
b64ac0539e | ||
![]() |
541c60b363 | ||
![]() |
824e95f7cb | ||
![]() |
38f7850196 | ||
![]() |
bef9de88e2 | ||
![]() |
48cd5e7c7f | ||
![]() |
3b44fda51c | ||
![]() |
dbfc9a5bb4 | ||
![]() |
1b758aa41a | ||
![]() |
43bdc70899 | ||
![]() |
fadda000a6 | ||
![]() |
45a8c91a5a | ||
![]() |
8e938f18be | ||
![]() |
ab1b364c54 | ||
![]() |
5ec65b2fb0 | ||
![]() |
926eced724 | ||
![]() |
f7f8802272 | ||
![]() |
c6910dff02 | ||
![]() |
ad299d0dbb | ||
![]() |
8b124d1050 | ||
![]() |
ff41080dbd | ||
![]() |
0e28606e3d | ||
![]() |
6a025ceee5 | ||
![]() |
6b2e53d6dc | ||
![]() |
b989aa5561 | ||
![]() |
f5b0b7ebd2 | ||
![]() |
16881ae076 | ||
![]() |
af04112656 | ||
![]() |
a2863112dc | ||
![]() |
f531e4dfc5 | ||
![]() |
8db9b32ba7 | ||
![]() |
dd5691cbef | ||
![]() |
de48b32af3 | ||
![]() |
600b5042a1 | ||
![]() |
aac77029da | ||
![]() |
e50205f557 | ||
![]() |
e227411d1f | ||
![]() |
2de0ed793f | ||
![]() |
cb0276f273 | ||
![]() |
562b3f17c9 | ||
![]() |
0f78f81c1c | ||
![]() |
6594937d0a | ||
![]() |
64bc6084be | ||
![]() |
20434d5bd2 | ||
![]() |
9ecc9380e6 | ||
![]() |
a57c55080b | ||
![]() |
b8ca06c6be | ||
![]() |
5aff6461a1 | ||
![]() |
6cf53fefec | ||
![]() |
45132f3503 | ||
![]() |
f1e78a0e8a | ||
![]() |
0bf28ec275 | ||
![]() |
41f8412c97 | ||
![]() |
c535974362 | ||
![]() |
1860c5f215 | ||
![]() |
6d778b2d39 | ||
![]() |
f48b99c259 | ||
![]() |
3c73b93051 | ||
![]() |
98f3f2d519 | ||
![]() |
b76b4e8d68 | ||
![]() |
07285a7c61 | ||
![]() |
03c0dfef37 | ||
![]() |
6ef6929c35 | ||
![]() |
e3c0c173f0 | ||
![]() |
7d64e058d4 | ||
![]() |
e97ee9b64b | ||
![]() |
6a03e39eeb | ||
![]() |
525ec740b5 | ||
![]() |
b790cf5f4e |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -365,4 +365,5 @@ FodyWeavers.xsd
|
|||||||
/src/*Pro*/
|
/src/*Pro*/
|
||||||
/src/*Pro*
|
/src/*Pro*
|
||||||
/src/*pro*
|
/src/*pro*
|
||||||
/src/*pro*/
|
/src/*pro*/
|
||||||
|
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
||||||
|
@@ -126,35 +126,8 @@ dotnet_style_qualification_for_property = false:silent
|
|||||||
dotnet_style_qualification_for_method = false:silent
|
dotnet_style_qualification_for_method = false:silent
|
||||||
dotnet_style_qualification_for_event = false:silent
|
dotnet_style_qualification_for_event = false:silent
|
||||||
|
|
||||||
|
dotnet_diagnostic.RCS1146.severity = warning
|
||||||
dotnet_diagnostic.CA2208.severity = none
|
dotnet_diagnostic.RCS1059.severity = none
|
||||||
dotnet_diagnostic.CA2008.severity = none
|
dotnet_diagnostic.RCS1138.severity = suggestion
|
||||||
dotnet_diagnostic.CA1812.severity = none
|
|
||||||
dotnet_diagnostic.CA1508.severity = none
|
|
||||||
dotnet_diagnostic.CA1512.severity = none
|
|
||||||
dotnet_diagnostic.CA1513.severity = none
|
|
||||||
dotnet_diagnostic.CA1810.severity = none
|
|
||||||
dotnet_diagnostic.CA1814.severity = none
|
|
||||||
dotnet_diagnostic.CA1815.severity = none
|
|
||||||
dotnet_diagnostic.CA1835.severity = none
|
|
||||||
dotnet_diagnostic.CA1819.severity = none
|
|
||||||
dotnet_diagnostic.CA1823.severity = none
|
|
||||||
dotnet_diagnostic.CA2002.severity = none
|
|
||||||
dotnet_diagnostic.CA5350.severity = none
|
|
||||||
dotnet_diagnostic.CA5351.severity = none
|
|
||||||
dotnet_diagnostic.CA5358.severity = none
|
|
||||||
dotnet_diagnostic.CA5384.severity = none
|
|
||||||
dotnet_diagnostic.CA5392.severity = none
|
|
||||||
dotnet_diagnostic.CA1805.severity = none
|
|
||||||
dotnet_diagnostic.CA1851.severity = none
|
|
||||||
dotnet_diagnostic.CA1510.severity = none
|
|
||||||
dotnet_diagnostic.CA5401.severity = none
|
|
||||||
dotnet_diagnostic.CA2022.severity = none
|
|
||||||
dotnet_diagnostic.CA1848.severity = none
|
|
||||||
dotnet_diagnostic.CA2000.severity = none
|
|
||||||
dotnet_diagnostic.CA5394.severity = none
|
|
||||||
dotnet_diagnostic.CA3003.severity = none
|
|
||||||
dotnet_diagnostic.CA1515.severity = none
|
|
||||||
dotnet_diagnostic.CA1849.severity = none
|
|
||||||
|
|
||||||
dotnet_code_quality.CA1822.api_surface = private, internal
|
dotnet_code_quality.CA1822.api_surface = private, internal
|
@@ -64,24 +64,31 @@ public sealed class OperDescAttribute : MoAttribute
|
|||||||
|
|
||||||
public override void OnException(MethodContext context)
|
public override void OnException(MethodContext context)
|
||||||
{
|
{
|
||||||
//插入异常日志
|
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
|
||||||
SysOperateLog log = GetOperLog(LocalizerType, context);
|
{
|
||||||
|
//插入异常日志
|
||||||
|
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||||
|
|
||||||
log.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
log.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
||||||
log.ExeStatus = false;//操作状态为失败
|
log.ExeStatus = false;//操作状态为失败
|
||||||
if (context.Exception is AppFriendlyException exception)
|
if (context.Exception is AppFriendlyException exception)
|
||||||
log.ExeMessage = exception?.Message;
|
log.ExeMessage = exception?.Message;
|
||||||
else
|
else
|
||||||
log.ExeMessage = context.Exception?.ToString();
|
log.ExeMessage = context.Exception?.ToString();
|
||||||
|
|
||||||
OperDescAttribute.WriteToQueue(log);
|
OperDescAttribute.WriteToQueue(log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnSuccess(MethodContext context)
|
public override void OnSuccess(MethodContext context)
|
||||||
{
|
{
|
||||||
//插入操作日志
|
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
|
||||||
SysOperateLog log = GetOperLog(LocalizerType, context);
|
{
|
||||||
OperDescAttribute.WriteToQueue(log);
|
|
||||||
|
//插入操作日志
|
||||||
|
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||||
|
OperDescAttribute.WriteToQueue(log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -115,7 +122,7 @@ public sealed class OperDescAttribute : MoAttribute
|
|||||||
private SysOperateLog GetOperLog(Type? localizerType, MethodContext context)
|
private SysOperateLog GetOperLog(Type? localizerType, MethodContext context)
|
||||||
{
|
{
|
||||||
var methodBase = context.Method;
|
var methodBase = context.Method;
|
||||||
var clientInfo = AppService.ClientInfo;
|
var userAgent = AppService.UserAgent;
|
||||||
string? paramJson = null;
|
string? paramJson = null;
|
||||||
if (IsRecordPar)
|
if (IsRecordPar)
|
||||||
{
|
{
|
||||||
@@ -127,10 +134,10 @@ public sealed class OperDescAttribute : MoAttribute
|
|||||||
{
|
{
|
||||||
parametersDict[parametersInfo[i].Name!] = args[i];
|
parametersDict[parametersInfo[i].Name!] = args[i];
|
||||||
}
|
}
|
||||||
paramJson = parametersDict.ToJsonNetString();
|
paramJson = parametersDict.ToSystemTextJsonString();
|
||||||
}
|
}
|
||||||
var result = context.ReturnValue;
|
var result = context.ReturnValue;
|
||||||
var resultJson = IsRecordPar ? result?.ToJsonNetString() : null;
|
var resultJson = IsRecordPar ? result?.ToSystemTextJsonString() : null;
|
||||||
//操作日志表实体
|
//操作日志表实体
|
||||||
var log = new SysOperateLog
|
var log = new SysOperateLog
|
||||||
{
|
{
|
||||||
@@ -138,8 +145,8 @@ public sealed class OperDescAttribute : MoAttribute
|
|||||||
Category = LogCateGoryEnum.Operate,
|
Category = LogCateGoryEnum.Operate,
|
||||||
ExeStatus = true,
|
ExeStatus = true,
|
||||||
OpIp = AppService?.RemoteIpAddress ?? string.Empty,
|
OpIp = AppService?.RemoteIpAddress ?? string.Empty,
|
||||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
OpBrowser = userAgent?.Browser,
|
||||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
OpOs = userAgent?.Platform,
|
||||||
OpTime = DateTime.Now,
|
OpTime = DateTime.Now,
|
||||||
OpAccount = UserManager.UserAccount,
|
OpAccount = UserManager.UserAccount,
|
||||||
ReqUrl = null,
|
ReqUrl = null,
|
||||||
|
@@ -13,14 +13,16 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 需要角色授权权限
|
/// 需要角色授权权限
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||||
public sealed class RolePermissionAttribute : Attribute
|
public sealed class RolePermissionAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 忽略角色授权权限
|
/// 忽略角色授权权限
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
|
||||||
public sealed class IgnoreRolePermissionAttribute : Attribute
|
public sealed class IgnoreRolePermissionAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public sealed class LoginLogAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public sealed class LogoutLogAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
@@ -11,7 +11,7 @@
|
|||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class CacheConst
|
public static class CacheConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Token表缓存Key
|
/// Token表缓存Key
|
||||||
|
@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通讯器常量
|
/// 通讯器常量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HubConst
|
public static class HubConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 系统HubUrl
|
/// 系统HubUrl
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// 资源表常量
|
/// 资源表常量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class ResourceConst
|
public static class ResourceConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 系统内置编码
|
/// 系统内置编码
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// 角色常量
|
/// 角色常量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class RoleConst
|
public static class RoleConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// api角色
|
/// api角色
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// SqlSugar系统常量
|
/// SqlSugar系统常量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class SqlSugarConst
|
public static class SqlSugarConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DB_Admin
|
/// DB_Admin
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
[ApiDescriptionSettings(false)]
|
[ApiDescriptionSettings(false)]
|
||||||
[Route("api/auth")]
|
[Route("api/auth")]
|
||||||
[LoggingMonitor]
|
[RequestAudit]
|
||||||
public class AuthController : ControllerBase
|
public class AuthController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
@@ -27,14 +28,31 @@ public class AuthController : ControllerBase
|
|||||||
|
|
||||||
[HttpPost("login")]
|
[HttpPost("login")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
|
[LoginLog]
|
||||||
public Task<LoginOutput> LoginAsync([FromBody] LoginInput input)
|
public Task<LoginOutput> LoginAsync([FromBody] LoginInput input)
|
||||||
{
|
{
|
||||||
|
|
||||||
return _authService.LoginAsync(input);
|
return _authService.LoginAsync(input);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("oauth-login")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[SuppressRequestAudit]
|
||||||
|
public IActionResult OAuthLogin(string scheme = "Gitee", string returnUrl = "/")
|
||||||
|
{
|
||||||
|
var props = new AuthenticationProperties
|
||||||
|
{
|
||||||
|
RedirectUri = returnUrl
|
||||||
|
};
|
||||||
|
return Challenge(props, scheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPost("logout")]
|
[HttpPost("logout")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[IgnoreRolePermission]
|
[IgnoreRolePermission]
|
||||||
|
[LogoutLog]
|
||||||
public Task LogoutAsync()
|
public Task LogoutAsync()
|
||||||
{
|
{
|
||||||
return _authService.LoginOutAsync();
|
return _authService.LoginOutAsync();
|
||||||
|
@@ -25,7 +25,8 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
[Description("登录")]
|
[Description("登录")]
|
||||||
[Route("openapi/auth")]
|
[Route("openapi/auth")]
|
||||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||||
[LoggingMonitor]
|
[RequestAudit]
|
||||||
|
[ApiController]
|
||||||
public class OpenApiController : ControllerBase
|
public class OpenApiController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
|
@@ -15,16 +15,13 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
[Route("api/[controller]/[action]")]
|
[Route("api/[controller]/[action]")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
|
[ApiController]
|
||||||
public class TestController : ControllerBase
|
public class TestController : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpPost]
|
[HttpGet]
|
||||||
public Task Test(string data)
|
public void Test()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; i++)
|
GC.Collect();
|
||||||
{
|
GC.WaitForPendingFinalizers();
|
||||||
GC.Collect();
|
|
||||||
GC.WaitForPendingFinalizers();
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace System.Logging;
|
||||||
|
|
||||||
|
public class RequestAudit
|
||||||
|
{
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
using ThingsGateway.DependencyInjection;
|
||||||
|
|
||||||
|
namespace System;
|
||||||
|
|
||||||
|
[SuppressSniffer, AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
|
||||||
|
public sealed class RequestAuditAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,101 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public class RequestAuditData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 分类
|
||||||
|
/// </summary>
|
||||||
|
public string CateGory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 客户端信息
|
||||||
|
/// </summary>
|
||||||
|
public UserAgent Client { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求方法:POST/GET
|
||||||
|
/// </summary>
|
||||||
|
public string Method { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作名称
|
||||||
|
/// </summary>
|
||||||
|
public string Operation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求地址
|
||||||
|
/// </summary>
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法名称
|
||||||
|
/// </summary>
|
||||||
|
public string ActionName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 认证信息
|
||||||
|
/// </summary>
|
||||||
|
public List<AuthorizationClaims> AuthorizationClaims { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控制器名
|
||||||
|
/// </summary>
|
||||||
|
public string ControllerName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异常信息
|
||||||
|
/// </summary>
|
||||||
|
public LogException Exception { get; set; }
|
||||||
|
|
||||||
|
public long TimeOperationElapsedMilliseconds { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务端
|
||||||
|
/// </summary>
|
||||||
|
public string LocalIPv4 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日志时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset LogDateTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参数列表
|
||||||
|
/// </summary>
|
||||||
|
public List<Parameters> Parameters { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 客户端IPV4地址
|
||||||
|
/// </summary>
|
||||||
|
public string RemoteIPv4 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求地址
|
||||||
|
/// </summary>
|
||||||
|
public string RequestUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回信息
|
||||||
|
/// </summary>
|
||||||
|
public object ReturnInformation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证错误信息
|
||||||
|
/// </summary>
|
||||||
|
public Validation Validation { get; set; }
|
||||||
|
public MethodInfo MethodInfo { get; set; }
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,301 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Logging;
|
||||||
|
|
||||||
|
using ThingsGateway.FriendlyException;
|
||||||
|
using ThingsGateway.Logging;
|
||||||
|
using ThingsGateway.NewLife.Json.Extension;
|
||||||
|
using ThingsGateway.UnifyResult;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
|
||||||
|
{
|
||||||
|
private const int FilterOrder = -3000;
|
||||||
|
public int Order => FilterOrder;
|
||||||
|
|
||||||
|
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||||
|
{
|
||||||
|
var timeOperation = Stopwatch.StartNew();
|
||||||
|
var resultContext = await next().ConfigureAwait(false);
|
||||||
|
// 计算接口执行时间
|
||||||
|
timeOperation.Stop();
|
||||||
|
|
||||||
|
var controllerActionDescriptor = (context.ActionDescriptor as ControllerActionDescriptor);
|
||||||
|
// 获取动作方法描述器
|
||||||
|
var actionMethod = controllerActionDescriptor?.MethodInfo;
|
||||||
|
|
||||||
|
|
||||||
|
// 处理 Blazor Server
|
||||||
|
if (actionMethod == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除 WebSocket 请求处理
|
||||||
|
if (context.HttpContext.IsWebSocketRequest())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果贴了 [SuppressMonitor] 特性则跳过
|
||||||
|
if (actionMethod.IsDefined(typeof(SuppressRequestAuditAttribute), true)
|
||||||
|
|| actionMethod.DeclaringType.IsDefined(typeof(SuppressRequestAuditAttribute), true))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 只有方法贴有特性才进行审计
|
||||||
|
if (
|
||||||
|
!actionMethod.DeclaringType.IsDefined(typeof(RequestAuditAttribute), true)
|
||||||
|
&&
|
||||||
|
!actionMethod.IsDefined(typeof(RequestAuditAttribute), true))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var logData = new RequestAuditData();
|
||||||
|
|
||||||
|
|
||||||
|
logData.TimeOperationElapsedMilliseconds = timeOperation.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
var resultHttpContext = (resultContext as FilterContext).HttpContext;
|
||||||
|
|
||||||
|
// 获取 HttpContext 和 HttpRequest 对象
|
||||||
|
var httpContext = context.HttpContext;
|
||||||
|
var httpRequest = httpContext.Request;
|
||||||
|
|
||||||
|
// 获取客户端 Ipv4 地址
|
||||||
|
var remoteIPv4 = httpContext.GetRemoteIpAddressToIPv4();
|
||||||
|
logData.RemoteIPv4 = remoteIPv4;
|
||||||
|
var requestUrl = Uri.UnescapeDataString(httpRequest.GetRequestUrlAddress());
|
||||||
|
logData.RequestUrl = requestUrl;
|
||||||
|
|
||||||
|
object returnValue = null;
|
||||||
|
Type finalReturnType;
|
||||||
|
var result = resultContext.Result as IActionResult;
|
||||||
|
// 解析返回值
|
||||||
|
if (UnifyContext.CheckVaildResult(result, out var data))
|
||||||
|
{
|
||||||
|
returnValue = data;
|
||||||
|
finalReturnType = data?.GetType();
|
||||||
|
}
|
||||||
|
// 处理文件类型
|
||||||
|
else if (result is FileResult fresult)
|
||||||
|
{
|
||||||
|
returnValue = new
|
||||||
|
{
|
||||||
|
FileName = fresult.FileDownloadName,
|
||||||
|
fresult.ContentType,
|
||||||
|
Length = fresult is FileContentResult cresult ? (object)cresult.FileContents.Length : null
|
||||||
|
};
|
||||||
|
finalReturnType = fresult?.GetType();
|
||||||
|
}
|
||||||
|
else finalReturnType = result?.GetType();
|
||||||
|
|
||||||
|
logData.ReturnInformation = returnValue;
|
||||||
|
|
||||||
|
//获取客户端信息
|
||||||
|
var client = App.GetService<IAppService>().UserAgent;
|
||||||
|
//操作名称默认是控制器名加方法名,自定义操作名称要在action上加Description特性
|
||||||
|
var option = $"{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}";
|
||||||
|
|
||||||
|
var desc = App.CreateLocalizerByType(controllerActionDescriptor.ControllerTypeInfo.AsType())[actionMethod.Name];
|
||||||
|
//获取特性
|
||||||
|
|
||||||
|
logData.CateGory = desc.Value;//传操作名称
|
||||||
|
logData.Operation = desc.Value;//传操作名称
|
||||||
|
logData.Client = client;
|
||||||
|
logData.Path = httpContext.Request.Path.Value;//请求地址
|
||||||
|
logData.Method = httpContext.Request.Method;//请求方法
|
||||||
|
logData.MethodInfo = actionMethod;//请求方法
|
||||||
|
|
||||||
|
logData.ControllerName = controllerActionDescriptor.ControllerName;
|
||||||
|
logData.ActionName = controllerActionDescriptor.ActionName;
|
||||||
|
|
||||||
|
logData.AuthorizationClaims = new();
|
||||||
|
// 获取授权用户
|
||||||
|
var user = httpContext.User;
|
||||||
|
foreach (var claim in user.Claims)
|
||||||
|
{
|
||||||
|
logData.AuthorizationClaims.Add(new AuthorizationClaims
|
||||||
|
{
|
||||||
|
Type = claim.Type,
|
||||||
|
Value = claim.Value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
logData.LocalIPv4 = httpContext.GetLocalIpAddressToIPv4();
|
||||||
|
logData.LogDateTime = DateTimeOffset.Now;
|
||||||
|
var parameterValues = context.ActionArguments;
|
||||||
|
|
||||||
|
logData.Parameters = new();
|
||||||
|
var parameters = actionMethod.GetParameters();
|
||||||
|
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
// 判断是否禁用记录特定参数
|
||||||
|
if (parameter.IsDefined(typeof(SuppressRequestAuditAttribute), false)) continue;
|
||||||
|
|
||||||
|
// 排除标记 [FromServices] 的解析
|
||||||
|
if (parameter.IsDefined(typeof(FromServicesAttribute), false)) continue;
|
||||||
|
|
||||||
|
var name = parameter.Name;
|
||||||
|
var parameterType = parameter.ParameterType;
|
||||||
|
|
||||||
|
_ = parameterValues.TryGetValue(name, out var value);
|
||||||
|
|
||||||
|
|
||||||
|
var par = new Parameters()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
};
|
||||||
|
logData.Parameters.Add(par);
|
||||||
|
|
||||||
|
object rawValue = default;
|
||||||
|
|
||||||
|
// 文件类型参数
|
||||||
|
if (value is IFormFile || value is List<IFormFile>)
|
||||||
|
{
|
||||||
|
// 单文件
|
||||||
|
if (value is IFormFile formFile)
|
||||||
|
{
|
||||||
|
var fileSize = Math.Round(formFile.Length / 1024D);
|
||||||
|
rawValue = new
|
||||||
|
{
|
||||||
|
name = formFile.Name,
|
||||||
|
fileName = formFile.FileName,
|
||||||
|
length = formFile.Length,
|
||||||
|
contentType = formFile.ContentType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 多文件
|
||||||
|
else if (value is List<IFormFile> formFiles)
|
||||||
|
{
|
||||||
|
var rawValues1 = new List<object>();
|
||||||
|
for (var i = 0; i < formFiles.Count; i++)
|
||||||
|
{
|
||||||
|
var file = formFiles[i];
|
||||||
|
var size = Math.Round(file.Length / 1024D);
|
||||||
|
var rawValue1 = new
|
||||||
|
{
|
||||||
|
name = file.Name,
|
||||||
|
fileName = file.FileName,
|
||||||
|
length = file.Length,
|
||||||
|
contentType = file.ContentType
|
||||||
|
};
|
||||||
|
rawValues1.Add(rawValue1);
|
||||||
|
}
|
||||||
|
rawValue = rawValues1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理 byte[] 参数类型
|
||||||
|
else if (value is byte[] byteArray)
|
||||||
|
{
|
||||||
|
rawValue = new
|
||||||
|
{
|
||||||
|
length = byteArray.Length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 处理基元类型,字符串类型和空值
|
||||||
|
else if (parameterType.IsPrimitive || value is string || value == null)
|
||||||
|
{
|
||||||
|
rawValue = value;
|
||||||
|
}
|
||||||
|
// 其他类型统一进行序列化
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rawValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
par.Value = rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取异常对象情况
|
||||||
|
Exception exception = resultContext.Exception;
|
||||||
|
if (exception is AppFriendlyException friendlyException)
|
||||||
|
{
|
||||||
|
logData.Validation = new();
|
||||||
|
logData.Validation.Message = friendlyException.Message;
|
||||||
|
}
|
||||||
|
else if (exception != null)
|
||||||
|
{
|
||||||
|
logData.Exception = new();
|
||||||
|
logData.Exception.Message = exception.Message;
|
||||||
|
logData.Exception.StackTrace = exception.StackTrace;
|
||||||
|
logData.Exception.Type = HandleGenericType(exception.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 创建日志记录器
|
||||||
|
var logger = httpContext.RequestServices.GetRequiredService<ILogger<RequestAudit>>();
|
||||||
|
|
||||||
|
var logContext = new LogContext();
|
||||||
|
|
||||||
|
logContext.Set(nameof(RequestAuditData), logData);
|
||||||
|
|
||||||
|
// 设置日志上下文
|
||||||
|
using var scope = logger.ScopeContext(logContext);
|
||||||
|
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception.ToSystemTextJsonString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理泛型类型转字符串打印问题
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string HandleGenericType(Type type)
|
||||||
|
{
|
||||||
|
if (type == null) return string.Empty;
|
||||||
|
|
||||||
|
var typeName = type.FullName ?? (!string.IsNullOrEmpty(type.Namespace) ? type.Namespace + "." : string.Empty) + type.Name;
|
||||||
|
|
||||||
|
// 处理泛型类型问题
|
||||||
|
if (type.IsConstructedGenericType)
|
||||||
|
{
|
||||||
|
var prefix = type.GetGenericArguments()
|
||||||
|
.Select(genericArg => HandleGenericType(genericArg))
|
||||||
|
.Aggregate((previous, current) => previous + ", " + current);
|
||||||
|
|
||||||
|
typeName = typeName.Split('`').First() + "<" + prefix + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
using ThingsGateway.DependencyInjection;
|
||||||
|
|
||||||
|
namespace System;
|
||||||
|
|
||||||
|
[SuppressSniffer, AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
|
||||||
|
public sealed class SuppressRequestAuditAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,277 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 只适合 Demo 登录,会直接授权超管的权限
|
||||||
|
/// </summary>
|
||||||
|
public class AdminOAuthHandler<TOptions>(
|
||||||
|
IVerificatInfoService verificatInfoService,
|
||||||
|
IAppService appService,
|
||||||
|
ISysUserService sysUserService,
|
||||||
|
ISysDictService configService,
|
||||||
|
IOptionsMonitor<TOptions> options,
|
||||||
|
ILoggerFactory logger,
|
||||||
|
IUserAgentService userAgentService,
|
||||||
|
UrlEncoder encoder
|
||||||
|
) : OAuthHandler<TOptions>(options, logger, encoder)
|
||||||
|
where TOptions : AdminOAuthOptions, new()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
static AdminOAuthHandler()
|
||||||
|
{
|
||||||
|
Task.Factory.StartNew(Insertable, TaskCreationOptions.LongRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日志消息队列(线程安全)
|
||||||
|
/// </summary>
|
||||||
|
protected static readonly ConcurrentQueue<SysOperateLog> _operateLogMessageQueue = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建访问日志
|
||||||
|
/// </summary>
|
||||||
|
private static async Task Insertable()
|
||||||
|
{
|
||||||
|
var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
|
||||||
|
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
|
||||||
|
while (!appLifetime.ApplicationStopping.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = _operateLogMessageQueue.ToListWithDequeue(); // 从日志队列中获取数据
|
||||||
|
if (data.Count > 0)
|
||||||
|
{
|
||||||
|
await db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);//入库
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
NewLife.Log.XTrace.WriteException(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await Task.Delay(3000, appLifetime.ApplicationStopping).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<AuthenticationTicket> CreateTicketAsync(
|
||||||
|
ClaimsIdentity identity,
|
||||||
|
AuthenticationProperties properties,
|
||||||
|
OAuthTokenResponse tokens)
|
||||||
|
{
|
||||||
|
properties.RedirectUri = Options.HomePath;
|
||||||
|
properties.IsPersistent = true;
|
||||||
|
var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
int expire = appConfig.LoginPolicy.VerificatExpireTime;
|
||||||
|
if (!string.IsNullOrEmpty(tokens.ExpiresIn) && int.TryParse(tokens.ExpiresIn, out var result))
|
||||||
|
{
|
||||||
|
properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result);
|
||||||
|
expire = (int)(result / 60.0);
|
||||||
|
}
|
||||||
|
var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var loginEvent = await GetLogin(expire).ConfigureAwait(false);
|
||||||
|
await UpdateUser(loginEvent).ConfigureAwait(false);
|
||||||
|
identity.AddClaim(new Claim(ClaimConst.VerificatId, loginEvent.VerificatId.ToString()));
|
||||||
|
identity.AddClaim(new Claim(ClaimConst.UserId, RoleConst.SuperAdminId.ToString()));
|
||||||
|
|
||||||
|
identity.AddClaim(new Claim(ClaimConst.SuperAdmin, "true"));
|
||||||
|
identity.AddClaim(new Claim(ClaimConst.OrgId, RoleConst.DefaultTenantId.ToString()));
|
||||||
|
identity.AddClaim(new Claim(ClaimConst.TenantId, RoleConst.DefaultTenantId.ToString()));
|
||||||
|
|
||||||
|
|
||||||
|
var context = new OAuthCreatingTicketContext(
|
||||||
|
new ClaimsPrincipal(identity),
|
||||||
|
properties,
|
||||||
|
Context,
|
||||||
|
Scheme,
|
||||||
|
Options,
|
||||||
|
Backchannel,
|
||||||
|
tokens,
|
||||||
|
user
|
||||||
|
);
|
||||||
|
|
||||||
|
context.RunClaimActions();
|
||||||
|
await Events.CreatingTicket(context).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var httpContext = context.HttpContext;
|
||||||
|
UserAgent? userAgent = null;
|
||||||
|
var str = httpContext?.Request?.Headers?.UserAgent;
|
||||||
|
if (!string.IsNullOrEmpty(str))
|
||||||
|
{
|
||||||
|
userAgent = userAgentService.Parse(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sysOperateLog = new SysOperateLog()
|
||||||
|
{
|
||||||
|
Name = this.Scheme.Name,
|
||||||
|
Category = LogCateGoryEnum.Login,
|
||||||
|
ExeStatus = true,
|
||||||
|
OpIp = httpContext.GetRemoteIpAddressToIPv4(),
|
||||||
|
OpBrowser = userAgent?.Browser,
|
||||||
|
OpOs = userAgent?.Platform,
|
||||||
|
OpTime = DateTime.Now,
|
||||||
|
VerificatId = loginEvent.VerificatId,
|
||||||
|
OpAccount = Options.GetName(user),
|
||||||
|
|
||||||
|
ReqMethod = "OAuth",
|
||||||
|
ReqUrl = string.Empty,
|
||||||
|
ResultJson = string.Empty,
|
||||||
|
ClassName = nameof(AdminOAuthHandler<TOptions>),
|
||||||
|
MethodName = string.Empty,
|
||||||
|
ParamJson = string.Empty,
|
||||||
|
};
|
||||||
|
_operateLogMessageQueue.Enqueue(sysOperateLog);
|
||||||
|
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>处理用户信息方法</summary>
|
||||||
|
protected virtual async Task<JsonElement> HandleUserInfoAsync(OAuthTokenResponse tokens)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, BuildUserInfoUrl(tokens));
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
|
||||||
|
var response = await Backchannel.SendAsync(request, Context.RequestAborted).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return JsonDocument.Parse(content).RootElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new OAuthTokenException($"OAuth user info endpoint failure: {await Display(response).ConfigureAwait(false)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>生成用户信息请求地址方法</summary>
|
||||||
|
protected virtual string BuildUserInfoUrl(OAuthTokenResponse tokens)
|
||||||
|
{
|
||||||
|
return QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "access_token", tokens.AccessToken }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>生成错误信息方法</summary>
|
||||||
|
protected static async Task<string> Display(HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
var output = new StringBuilder();
|
||||||
|
output.Append($"Status: {response.StatusCode}; ");
|
||||||
|
output.Append($"Headers: {response.Headers}; ");
|
||||||
|
output.Append($"Body: {await response.Content.ReadAsStringAsync().ConfigureAwait(false)};");
|
||||||
|
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<LoginEvent> GetLogin(int expire)
|
||||||
|
{
|
||||||
|
var sysUser = await sysUserService.GetUserByIdAsync(RoleConst.SuperAdminId).ConfigureAwait(false);//获取用户信息
|
||||||
|
|
||||||
|
var loginEvent = new LoginEvent
|
||||||
|
{
|
||||||
|
Ip = appService.RemoteIpAddress,
|
||||||
|
Device = appService.UserAgent?.Platform,
|
||||||
|
Expire = expire,
|
||||||
|
SysUser = sysUser,
|
||||||
|
VerificatId = CommonUtils.GetSingleId()
|
||||||
|
};
|
||||||
|
|
||||||
|
//获取verificat列表
|
||||||
|
var tokenTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
|
||||||
|
//生成verificat信息
|
||||||
|
var verificatInfo = new VerificatInfo
|
||||||
|
{
|
||||||
|
Device = loginEvent.Device,
|
||||||
|
Expire = loginEvent.Expire,
|
||||||
|
VerificatTimeout = tokenTimeout,
|
||||||
|
Id = loginEvent.VerificatId,
|
||||||
|
UserId = loginEvent.SysUser.Id,
|
||||||
|
LoginIp = loginEvent.Ip,
|
||||||
|
LoginTime = loginEvent.DateTime
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//添加到verificat列表
|
||||||
|
verificatInfoService.Add(verificatInfo);
|
||||||
|
return loginEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loginEvent"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task UpdateUser(LoginEvent loginEvent)
|
||||||
|
{
|
||||||
|
var sysUser = loginEvent.SysUser;
|
||||||
|
|
||||||
|
#region 登录/密码策略
|
||||||
|
|
||||||
|
var key = CacheConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值
|
||||||
|
App.CacheService.Remove(key);//移除登录错误次数
|
||||||
|
|
||||||
|
//获取用户verificat列表
|
||||||
|
var userToken = verificatInfoService.GetOne(loginEvent.VerificatId);
|
||||||
|
|
||||||
|
#endregion 登录/密码策略
|
||||||
|
|
||||||
|
#region 重新赋值属性,设置本次登录信息为最新的信息
|
||||||
|
|
||||||
|
sysUser.LastLoginIp = sysUser.LatestLoginIp;
|
||||||
|
sysUser.LastLoginTime = sysUser.LatestLoginTime;
|
||||||
|
sysUser.LatestLoginIp = loginEvent.Ip;
|
||||||
|
sysUser.LatestLoginTime = loginEvent.DateTime;
|
||||||
|
|
||||||
|
#endregion 重新赋值属性,设置本次登录信息为最新的信息
|
||||||
|
|
||||||
|
using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew();
|
||||||
|
//更新用户登录信息
|
||||||
|
if (await db.Updateable(sysUser).UpdateColumns(it => new
|
||||||
|
{
|
||||||
|
it.LastLoginIp,
|
||||||
|
it.LastLoginTime,
|
||||||
|
it.LatestLoginIp,
|
||||||
|
it.LatestLoginTime,
|
||||||
|
}).ExecuteCommandAsync().ConfigureAwait(false) > 0)
|
||||||
|
App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Cache信息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>自定义 Token 异常</summary>
|
||||||
|
public class OAuthTokenException : Exception
|
||||||
|
{
|
||||||
|
public OAuthTokenException() : base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OAuthTokenException(string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OAuthTokenException(string? message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
/// <summary>OAuthOptions 配置类</summary>
|
||||||
|
public abstract class AdminOAuthOptions : OAuthOptions
|
||||||
|
{
|
||||||
|
/// <summary>默认构造函数</summary>
|
||||||
|
protected AdminOAuthOptions()
|
||||||
|
{
|
||||||
|
ConfigureClaims();
|
||||||
|
this.Events.OnRemoteFailure = context =>
|
||||||
|
{
|
||||||
|
var redirectUri = string.IsNullOrEmpty(HomePath) ? "/" : HomePath;
|
||||||
|
context.Response.Redirect(redirectUri);
|
||||||
|
context.HandleResponse();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>配置 Claims 映射</summary>
|
||||||
|
protected virtual void ConfigureClaims()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetName(JsonElement element)
|
||||||
|
{
|
||||||
|
JsonElement.ObjectEnumerator target = element.EnumerateObject();
|
||||||
|
return target.TryGetValue("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>获得/设置 登陆后首页</summary>
|
||||||
|
public string HomePath { get; set; } = "/";
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,114 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public class GiteeOAuthOptions : AdminOAuthOptions
|
||||||
|
{
|
||||||
|
|
||||||
|
public GiteeOAuthOptions() : base()
|
||||||
|
{
|
||||||
|
this.SignInScheme = ClaimConst.Scheme;
|
||||||
|
this.AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
|
||||||
|
this.TokenEndpoint = "https://gitee.com/oauth/token";
|
||||||
|
this.UserInformationEndpoint = "https://gitee.com/api/v5/user";
|
||||||
|
this.HomePath = "/";
|
||||||
|
this.CallbackPath = "/signin-gitee";
|
||||||
|
Scope.Add("user_info");
|
||||||
|
Scope.Add("projects");
|
||||||
|
|
||||||
|
Events.OnCreatingTicket = async context =>
|
||||||
|
{
|
||||||
|
await HandlerGiteeStarredUrl(context).ConfigureAwait(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
Events.OnRedirectToAuthorizationEndpoint = context =>
|
||||||
|
{
|
||||||
|
//context.RedirectUri = context.RedirectUri.Replace("http%3A%2F%2F", "https%3A%2F%2F"); // 强制替换
|
||||||
|
context.Response.Redirect(context.RedirectUri);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>刷新 Token 方法</summary>
|
||||||
|
protected virtual async Task<OAuthTokenResponse> RefreshTokenAsync(TicketReceivedContext ticketReceivedContext, string refreshToken)
|
||||||
|
{
|
||||||
|
var query = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "refresh_token", refreshToken },
|
||||||
|
{ "grant_type", "refresh_token" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, QueryHelpers.AddQueryString(TokenEndpoint, query));
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
|
||||||
|
var response = await Backchannel.SendAsync(request, ticketReceivedContext.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return OAuthTokenResponse.Success(JsonDocument.Parse(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
return OAuthTokenResponse.Failed(new OAuthTokenException($"OAuth token endpoint failure: {await Display(response).ConfigureAwait(false)}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>生成错误信息方法</summary>
|
||||||
|
protected static async Task<string> Display(HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
var output = new StringBuilder();
|
||||||
|
output.Append($"Status: {response.StatusCode}; ");
|
||||||
|
output.Append($"Headers: {response.Headers}; ");
|
||||||
|
output.Append($"Body: {await response.Content.ReadAsStringAsync().ConfigureAwait(false)};");
|
||||||
|
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetName(JsonElement element)
|
||||||
|
{
|
||||||
|
JsonElement.ObjectEnumerator target = element.EnumerateObject();
|
||||||
|
return target.TryGetValue("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandlerGiteeStarredUrl(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(context.AccessToken))
|
||||||
|
throw new InvalidOperationException("Access token is missing.");
|
||||||
|
|
||||||
|
var uri = $"https://gitee.com/api/v5/user/starred/{repoFullName}";
|
||||||
|
|
||||||
|
var queryString = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "access_token", context.AccessToken }
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Put, QueryHelpers.AddQueryString(uri, queryString))
|
||||||
|
{
|
||||||
|
Headers = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } }
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new Exception($"Failed to star repository: {response.StatusCode}, {content}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
protected override void ConfigureClaims()
|
||||||
|
{
|
||||||
|
ClaimActions.MapJsonKey(ClaimConst.AvatarUrl, "avatar_url");
|
||||||
|
ClaimActions.MapJsonKey(ClaimConst.Account, "name");
|
||||||
|
|
||||||
|
base.ConfigureClaims();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public class GiteeOAuthSettings
|
||||||
|
{
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
public string ClientSecret { get; set; }
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public class GiteeOAuthUser
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public string Login { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Avatar_Url { get; set; }
|
||||||
|
}
|
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public static class OAuthUserExtensions
|
||||||
|
{
|
||||||
|
public static GiteeOAuthUser ToAuthUser(this JsonElement element)
|
||||||
|
{
|
||||||
|
GiteeOAuthUser authUser = new GiteeOAuthUser();
|
||||||
|
JsonElement.ObjectEnumerator target = element.EnumerateObject();
|
||||||
|
authUser.Id = target.TryGetValue("id");
|
||||||
|
authUser.Login = target.TryGetValue("login");
|
||||||
|
authUser.Name = target.TryGetValue("name");
|
||||||
|
authUser.Avatar_Url = target.TryGetValue("avatar_url");
|
||||||
|
return authUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string TryGetValue(this JsonElement.ObjectEnumerator target, string propertyName)
|
||||||
|
{
|
||||||
|
return target.FirstOrDefault<JsonProperty>((Func<JsonProperty, bool>)(t => t.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))).Value.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
@@ -51,7 +51,7 @@ public class HardwareInfo
|
|||||||
/// 进程占用内存
|
/// 进程占用内存
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoGenerateColumn(Ignore = true)]
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public string WorkingSet { get; set; }
|
public int WorkingSet { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新时间
|
/// 更新时间
|
||||||
|
@@ -17,6 +17,7 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
using ThingsGateway.Extension;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
using ThingsGateway.NewLife.Caching;
|
||||||
using ThingsGateway.NewLife.Threading;
|
using ThingsGateway.NewLife.Threading;
|
||||||
using ThingsGateway.Schedule;
|
using ThingsGateway.Schedule;
|
||||||
|
|
||||||
@@ -51,11 +52,20 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
|
|
||||||
#endregion 属性
|
#endregion 属性
|
||||||
|
|
||||||
|
private MemoryCache MemoryCache = new() { };
|
||||||
|
private const string CacheKey = "HistoryHardwareInfo";
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos()
|
public async Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos()
|
||||||
{
|
{
|
||||||
using var db = DbContext.Db.GetConnectionScopeWithAttr<HistoryHardwareInfo>().CopyNew();
|
var historyHardwareInfos = MemoryCache.Get<List<HistoryHardwareInfo>>(CacheKey);
|
||||||
return await db.Queryable<HistoryHardwareInfo>().ToListAsync().ConfigureAwait(false);
|
if (historyHardwareInfos == null)
|
||||||
|
{
|
||||||
|
using var db = DbContext.Db.GetConnectionScopeWithAttr<HistoryHardwareInfo>().CopyNew();
|
||||||
|
historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
MemoryCache.Set(CacheKey, historyHardwareInfos);
|
||||||
|
}
|
||||||
|
return historyHardwareInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool error = false;
|
private bool error = false;
|
||||||
@@ -94,7 +104,7 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
{
|
{
|
||||||
HardwareInfo.MachineInfo.Refresh();
|
HardwareInfo.MachineInfo.Refresh();
|
||||||
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
|
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
|
||||||
HardwareInfo.WorkingSet = (Environment.WorkingSet / 1024.0 / 1024.0).ToString("F2");
|
HardwareInfo.WorkingSet = (Environment.WorkingSet / 1024.0 / 1024.0).ToInt();
|
||||||
error = false;
|
error = false;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -116,17 +126,22 @@ public class HardwareJob : IJob, IHardwareJob
|
|||||||
var his = new HistoryHardwareInfo()
|
var his = new HistoryHardwareInfo()
|
||||||
{
|
{
|
||||||
Date = TimerX.Now,
|
Date = TimerX.Now,
|
||||||
DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToString("F2"),
|
DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(),
|
||||||
Battery = (HardwareInfo.MachineInfo.Battery * 100).ToString("F2"),
|
Battery = (HardwareInfo.MachineInfo.Battery * 100).ToInt(),
|
||||||
MemoryUsage = (HardwareInfo.WorkingSet),
|
MemoryUsage = (HardwareInfo.WorkingSet),
|
||||||
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToString("F2"),
|
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(),
|
||||||
Temperature = (HardwareInfo.MachineInfo.Temperature).ToString("F2"),
|
Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(),
|
||||||
};
|
};
|
||||||
await db.Insertable(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
await db.Insertable(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
||||||
|
MemoryCache.Remove(CacheKey);
|
||||||
}
|
}
|
||||||
var sevenDaysAgo = TimerX.Now.AddDays(-HardwareInfoOptions.DaysAgo);
|
var sevenDaysAgo = TimerX.Now.AddDays(-HardwareInfoOptions.DaysAgo);
|
||||||
//删除特定信息
|
//删除特定信息
|
||||||
await db.Deleteable<HistoryHardwareInfo>(a => a.Date <= sevenDaysAgo).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
var result = await db.Deleteable<HistoryHardwareInfo>(a => a.Date <= sevenDaysAgo).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
MemoryCache.Remove(CacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
error = false;
|
error = false;
|
||||||
|
@@ -19,23 +19,23 @@ public class HistoryHardwareInfo
|
|||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "磁盘使用率")]
|
[SugarColumn(ColumnDescription = "磁盘使用率")]
|
||||||
public string DriveUsage { get; set; }
|
public int DriveUsage { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "内存")]
|
[SugarColumn(ColumnDescription = "内存")]
|
||||||
public string MemoryUsage { get; set; }
|
public int MemoryUsage { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "CPU使用率")]
|
[SugarColumn(ColumnDescription = "CPU使用率")]
|
||||||
public string CpuUsage { get; set; }
|
public int CpuUsage { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "温度")]
|
[SugarColumn(ColumnDescription = "温度")]
|
||||||
public string Temperature { get; set; }
|
public int Temperature { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "电池")]
|
[SugarColumn(ColumnDescription = "电池")]
|
||||||
public string Battery { get; set; }
|
public int Battery { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[SugarColumn(ColumnDescription = "时间")]
|
[SugarColumn(ColumnDescription = "时间")]
|
||||||
|
@@ -1,4 +1,15 @@
|
|||||||
{
|
{
|
||||||
|
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||||
|
"CreateOrgId": "CreateOrgId"
|
||||||
|
},
|
||||||
|
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||||
|
"SortCode": "SortCode",
|
||||||
|
"CreateTime": "CreateTime",
|
||||||
|
"CreateUser": "CreateUser",
|
||||||
|
"UpdateTime": "UpdateTime",
|
||||||
|
"UpdateUser": "UpdateUser"
|
||||||
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||||
"UserExpire": "User expired, please login again"
|
"UserExpire": "User expired, please login again"
|
||||||
},
|
},
|
||||||
@@ -24,9 +35,6 @@
|
|||||||
"LatestLoginTime": "LatestLoginTime",
|
"LatestLoginTime": "LatestLoginTime",
|
||||||
"LatestLoginDevice": "LatestLoginDevice",
|
"LatestLoginDevice": "LatestLoginDevice",
|
||||||
"LatestLoginAddress": "LatestLoginAddress",
|
"LatestLoginAddress": "LatestLoginAddress",
|
||||||
"SortCode": "SortCode",
|
|
||||||
"CreateTime": "CreateTime",
|
|
||||||
"UpdateTime": "UpdateTime",
|
|
||||||
"OrgNames": "OrgNames",
|
"OrgNames": "OrgNames",
|
||||||
"PositionName": "PositionName",
|
"PositionName": "PositionName",
|
||||||
"OrgId": "Org",
|
"OrgId": "Org",
|
||||||
@@ -60,9 +68,6 @@
|
|||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Name.Required": "{0} is required",
|
"Name.Required": "{0} is required",
|
||||||
"Category": "Category",
|
"Category": "Category",
|
||||||
"SortCode": "Sort",
|
|
||||||
"CreateTime": "CreateTime",
|
|
||||||
"UpdateTime": "UpdateTime",
|
|
||||||
"OrgId": "Org",
|
"OrgId": "Org",
|
||||||
"Global": "Global",
|
"Global": "Global",
|
||||||
"Status": "Status",
|
"Status": "Status",
|
||||||
@@ -105,9 +110,6 @@
|
|||||||
"Category": "Category",
|
"Category": "Category",
|
||||||
"Target": "Target",
|
"Target": "Target",
|
||||||
"NavLinkMatch": "NavLinkMatch",
|
"NavLinkMatch": "NavLinkMatch",
|
||||||
"SortCode": "Sort",
|
|
||||||
"CreateTime": "CreateTime",
|
|
||||||
"UpdateTime": "UpdateTime",
|
|
||||||
"ParentId": "Parent",
|
"ParentId": "Parent",
|
||||||
"ResourceDup": "Duplicate name {0} exists",
|
"ResourceDup": "Duplicate name {0} exists",
|
||||||
"ResourceParentChoiceSelf": "Parent cannot choose itself",
|
"ResourceParentChoiceSelf": "Parent cannot choose itself",
|
||||||
@@ -134,9 +136,6 @@
|
|||||||
"Status": "Status",
|
"Status": "Status",
|
||||||
"OrgId": "Organization",
|
"OrgId": "Organization",
|
||||||
"Remark": "Remarks",
|
"Remark": "Remarks",
|
||||||
"SortCode": "SortCode",
|
|
||||||
"CreateTime": "CreateTime",
|
|
||||||
"UpdateTime": "UpdateTime",
|
|
||||||
"Dup": "Duplicate position exists with Category {0} and Name {1}",
|
"Dup": "Duplicate position exists with Category {0} and Name {1}",
|
||||||
"CodeDup": "Duplicate code {0} exists",
|
"CodeDup": "Duplicate code {0} exists",
|
||||||
"NameDup": "Duplicate name {0} exists",
|
"NameDup": "Duplicate name {0} exists",
|
||||||
@@ -159,9 +158,6 @@
|
|||||||
"Names": "Names",
|
"Names": "Names",
|
||||||
"Remark": "Remarks",
|
"Remark": "Remarks",
|
||||||
"DirectorId": "Director",
|
"DirectorId": "Director",
|
||||||
"SortCode": "SortCode",
|
|
||||||
"CreateTime": "CreateTime",
|
|
||||||
"UpdateTime": "UpdateTime",
|
|
||||||
"Dup": "Duplicate organization exists with Category {0} and Name {1}",
|
"Dup": "Duplicate organization exists with Category {0} and Name {1}",
|
||||||
"CodeDup": "Duplicate code {0} exists",
|
"CodeDup": "Duplicate code {0} exists",
|
||||||
"NameDup": "Duplicate name {0} exists",
|
"NameDup": "Duplicate name {0} exists",
|
||||||
@@ -358,9 +354,6 @@
|
|||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Code": "Code",
|
"Code": "Code",
|
||||||
"Remark": "Remark",
|
"Remark": "Remark",
|
||||||
"SortCode": "Sort",
|
|
||||||
"CreateTime": "CreateTime",
|
|
||||||
"UpdateTime": "UpdateTime",
|
|
||||||
"DemoCanotUpdateWebsitePolicy": "DEMO environment does not allow modifying website settings",
|
"DemoCanotUpdateWebsitePolicy": "DEMO environment does not allow modifying website settings",
|
||||||
"DictDup": "Duplicate configuration exists, category {0}, name {1}"
|
"DictDup": "Duplicate configuration exists, category {0}, name {1}"
|
||||||
},
|
},
|
||||||
|
@@ -1,469 +1,462 @@
|
|||||||
{
|
{
|
||||||
|
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||||
|
"CreateOrgId": "创建机构Id"
|
||||||
|
},
|
||||||
|
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||||
|
"SortCode": "排序",
|
||||||
|
"CreateTime": "创建时间",
|
||||||
|
"CreateUser": "创建人",
|
||||||
|
"UpdateTime": "更新时间",
|
||||||
|
"UpdateUser": "更新人"
|
||||||
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||||
"UserExpire": "用户登录已过期,请重新登录"
|
"UserExpire": "用户登录已过期,请重新登录"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.SysUser": {
|
"ThingsGateway.Admin.Application.SysUser": {
|
||||||
"Disable": "禁用",
|
"Disable": "禁用",
|
||||||
"Enable": "启用",
|
"Enable": "启用",
|
||||||
"GrantRole": "分配角色",
|
"GrantRole": "分配角色",
|
||||||
"ExitVerificat": "您已被强制下线",
|
"ExitVerificat": "您已被强制下线",
|
||||||
"PasswordEdited": "密码被修改,已退出登录",
|
"PasswordEdited": "密码被修改,已退出登录",
|
||||||
"Avatar": "头像",
|
"Avatar": "头像",
|
||||||
"Account": "账号",
|
"Account": "账号",
|
||||||
"Account.Required": " {0} 是必填项",
|
"Account.Required": " {0} 是必填项",
|
||||||
"Password": "密码",
|
"Password": "密码",
|
||||||
"Status": "状态",
|
"Status": "状态",
|
||||||
"Phone": "手机",
|
"Phone": "手机",
|
||||||
"Email": "邮箱",
|
"Email": "邮箱",
|
||||||
"LastLoginIp": "上次登录ip",
|
"LastLoginIp": "上次登录ip",
|
||||||
"LastLoginDevice": "上次登录设备",
|
"LastLoginDevice": "上次登录设备",
|
||||||
"LastLoginTime": "上次登录时间",
|
"LastLoginTime": "上次登录时间",
|
||||||
"LastLoginAddress": "上次登录地点",
|
"LastLoginAddress": "上次登录地点",
|
||||||
"LatestLoginIp": "最新登录ip",
|
"LatestLoginIp": "最新登录ip",
|
||||||
"LatestLoginTime": "最新登录时间",
|
"LatestLoginTime": "最新登录时间",
|
||||||
"LatestLoginDevice": "最新登录设备",
|
"LatestLoginDevice": "最新登录设备",
|
||||||
"LatestLoginAddress": "最新登录地点",
|
"LatestLoginAddress": "最新登录地点",
|
||||||
"SortCode": "排序",
|
"OrgNames": "机构名称",
|
||||||
"CreateTime": "创建时间",
|
"PositionName": "职位名称",
|
||||||
"UpdateTime": "更新时间",
|
"OrgId": "机构",
|
||||||
"OrgNames": "机构名称",
|
"PositionId": "职位",
|
||||||
"PositionName": "职位名称",
|
"DirectorId": "主管",
|
||||||
"OrgId": "机构",
|
"CheckSelf": "禁止 {0} 自己",
|
||||||
"PositionId": "职位",
|
"CanotDeleteAdminUser": "不可删除系统内置超管用户",
|
||||||
"DirectorId": "主管",
|
"CanotEditAdminUser": "不可编辑超管用户",
|
||||||
"CheckSelf": "禁止 {0} 自己",
|
"CanotGrantAdmin": "不能分配超管角色",
|
||||||
"CanotDeleteAdminUser": "不可删除系统内置超管用户",
|
"EmailDup": "存在重复的邮箱 {0}",
|
||||||
"CanotEditAdminUser": "不可编辑超管用户",
|
"AccountDup": "存在重复的账号 {0}",
|
||||||
"CanotGrantAdmin": "不能分配超管角色",
|
"CanotDeleteSelf": "不可删除自己",
|
||||||
"EmailDup": "存在重复的邮箱 {0}",
|
"EmailError": "邮箱 {0} 格式错误",
|
||||||
"AccountDup": "存在重复的账号 {0}",
|
"PhoneError": "手机号码 {0} 格式错误",
|
||||||
"CanotDeleteSelf": "不可删除自己",
|
"NoOrg": "组织机构不存在",
|
||||||
"EmailError": "邮箱 {0} 格式错误",
|
"DirectorSelf": "不能设置自己为主管",
|
||||||
"PhoneError": "手机号码 {0} 格式错误",
|
|
||||||
"NoOrg": "组织机构不存在",
|
|
||||||
"DirectorSelf": "不能设置自己为主管",
|
|
||||||
|
|
||||||
|
|
||||||
"DemoCanotUpdatePassword": "DEMO环境不允许修改密码",
|
"DemoCanotUpdatePassword": "DEMO环境不允许修改密码",
|
||||||
"OldPasswordError": "原密码错误",
|
"OldPasswordError": "原密码错误",
|
||||||
"ConfirmPasswordDiff": "两次输入的密码不一致",
|
"ConfirmPasswordDiff": "两次输入的密码不一致",
|
||||||
"PasswordLengthLess": "密码长度不能小于 {0} ",
|
"PasswordLengthLess": "密码长度不能小于 {0} ",
|
||||||
"PasswordMustNum ": "密码必须包含数字",
|
"PasswordMustNum ": "密码必须包含数字",
|
||||||
"PasswordMustLow": "密码必须包含小写字母",
|
"PasswordMustLow": "密码必须包含小写字母",
|
||||||
"PasswordMustUpp": "密码必须包含大写字母",
|
"PasswordMustUpp": "密码必须包含大写字母",
|
||||||
"PasswordMustSpecial": "密码必须包含特殊字符"
|
"PasswordMustSpecial": "密码必须包含特殊字符"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.SysRole": {
|
"ThingsGateway.Admin.Application.SysRole": {
|
||||||
"Code": "编码",
|
"Code": "编码",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
"Name.Required": " {0} 是必填项",
|
"Name.Required": " {0} 是必填项",
|
||||||
"Category": "分类",
|
"Category": "分类",
|
||||||
"SortCode": "排序",
|
"Global": "全局",
|
||||||
"Global": "全局",
|
"Status": "状态",
|
||||||
"Status": "状态",
|
"OrgId": "机构",
|
||||||
"OrgId": "机构",
|
|
||||||
"CreateTime": "创建时间",
|
|
||||||
"UpdateTime": "更新时间",
|
|
||||||
|
|
||||||
"CanotDeleteAdmin": "不可删除系统内置超管角色",
|
"CanotDeleteAdmin": "不可删除系统内置超管角色",
|
||||||
"CanotEditAdmin": "不可编辑超管角色",
|
"CanotEditAdmin": "不可编辑超管角色",
|
||||||
"CanotGrantAdmin": "不能分配超管角色",
|
"CanotGrantAdmin": "不能分配超管角色",
|
||||||
|
|
||||||
"NameDup": "存在重复的角色名称 {0}",
|
"NameDup": "存在重复的角色名称 {0}",
|
||||||
"OrgNotNull": "机构不能为空",
|
"OrgNotNull": "机构不能为空",
|
||||||
"SameOrgNameDup": "存在重复的角色名称 {0}",
|
"SameOrgNameDup": "存在重复的角色名称 {0}",
|
||||||
"CannotRoleScopeAll": "机构角色不能选择全局数据范围",
|
"CannotRoleScopeAll": "机构角色不能选择全局数据范围",
|
||||||
"CodeDup": "存在重复的编码 {0}"
|
"CodeDup": "存在重复的编码 {0}"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
|
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
|
||||||
"Global": "全局",
|
"Global": "全局",
|
||||||
"Org": "机构"
|
"Org": "机构"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.DataScopeEnum": {
|
"ThingsGateway.Admin.Application.DataScopeEnum": {
|
||||||
"SCOPE_SELF": "仅自己",
|
"SCOPE_SELF": "仅自己",
|
||||||
"SCOPE_ALL": "全部",
|
"SCOPE_ALL": "全部",
|
||||||
"SCOPE_ORG": "仅所属组织",
|
"SCOPE_ORG": "仅所属组织",
|
||||||
"SCOPE_ORG_CHILD": "所属组织及以下",
|
"SCOPE_ORG_CHILD": "所属组织及以下",
|
||||||
"SCOPE_ORG_DEFINE": "自定义"
|
"SCOPE_ORG_DEFINE": "自定义"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.DefaultDataScope": {
|
"ThingsGateway.Admin.Application.DefaultDataScope": {
|
||||||
"ScopeCategory": "数据范围",
|
"ScopeCategory": "数据范围",
|
||||||
"ScopeDefineOrgIdList": "自定义列表"
|
"ScopeDefineOrgIdList": "自定义列表"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.SysResource": {
|
"ThingsGateway.Admin.Application.SysResource": {
|
||||||
"Title": "标题",
|
"Title": "标题",
|
||||||
"Module": "模块",
|
"Module": "模块",
|
||||||
"Title.Required": "{0} 是必填项",
|
"Title.Required": "{0} 是必填项",
|
||||||
"Href.Required": "{0} 是必填项",
|
"Href.Required": "{0} 是必填项",
|
||||||
"Icon": "图标",
|
"Icon": "图标",
|
||||||
"Href": "路径",
|
"Href": "路径",
|
||||||
"Code": "编码",
|
"Code": "编码",
|
||||||
"Category": "分类",
|
"Category": "分类",
|
||||||
"Target": "跳转类型",
|
"Target": "跳转类型",
|
||||||
"NavLinkMatch": "匹配类型",
|
"NavLinkMatch": "匹配类型",
|
||||||
"SortCode": "排序",
|
"ParentId": "上级菜单",
|
||||||
"ParentId": "上级菜单",
|
"ResourceDup": "存在重复的名称 {0}",
|
||||||
"CreateTime": "创建时间",
|
"ResourceParentChoiceSelf": "父级不能选择自己",
|
||||||
"UpdateTime": "更新时间",
|
"ResourceParentNull": "父级不存在 {0}",
|
||||||
"ResourceDup": "存在重复的名称 {0}",
|
"NotFoundResource": "系统异常,没找到该菜单",
|
||||||
"ResourceParentChoiceSelf": "父级不能选择自己",
|
"ModuleIdDiff": "模块与上级菜单不一致",
|
||||||
"ResourceParentNull": "父级不存在 {0}",
|
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
|
||||||
"NotFoundResource": "系统异常,没找到该菜单",
|
"ResourceMenuHrefNotNull": "菜单的路径不能为空"
|
||||||
"ModuleIdDiff": "模块与上级菜单不一致",
|
},
|
||||||
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
|
|
||||||
"ResourceMenuHrefNotNull": "菜单的路径不能为空"
|
|
||||||
},
|
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
|
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
|
||||||
"TargetId": "目标机构",
|
"TargetId": "目标机构",
|
||||||
"ContainsChild": "包含下级",
|
"ContainsChild": "包含下级",
|
||||||
"ContainsPosition": "包含职位"
|
"ContainsPosition": "包含职位"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.SysPosition": {
|
"ThingsGateway.Admin.Application.SysPosition": {
|
||||||
"Category.Required": "{0} 是必填项",
|
"Category.Required": "{0} 是必填项",
|
||||||
"Name.Required": "{0} 是必填项",
|
"Name.Required": "{0} 是必填项",
|
||||||
"Code.Required": "{0} 是必填项",
|
"Code.Required": "{0} 是必填项",
|
||||||
"OrgId.MinValue": "{0} 是必填项",
|
"OrgId.MinValue": "{0} 是必填项",
|
||||||
"Category": "分类",
|
"Category": "分类",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
"Code": "代码",
|
"Code": "代码",
|
||||||
"Status": "状态",
|
"Status": "状态",
|
||||||
"OrgId": "机构",
|
"OrgId": "机构",
|
||||||
"Remark": "备注",
|
"Remark": "备注",
|
||||||
"SortCode": "排序",
|
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
|
||||||
"CreateTime": "创建时间",
|
"CodeDup": "存在重复的编码 {0}",
|
||||||
"UpdateTime": "更新时间",
|
"NameDup": "存在重复的名称 {0}",
|
||||||
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
|
"CanotContainsSelf": "不可包含自己",
|
||||||
"CodeDup": "存在重复的编码 {0}",
|
"TargetNameDup": "目标节点存在重复的名称 {0}",
|
||||||
"NameDup": "存在重复的名称 {0}",
|
"ParentChoiceSelf": "父级不能选择自己",
|
||||||
"CanotContainsSelf": "不可包含自己",
|
"ParentNull": "父级不存在 {0}",
|
||||||
"TargetNameDup": "目标节点存在重复的名称 {0}",
|
"DeleteUserFirst": "请先删除职位下的用户"
|
||||||
"ParentChoiceSelf": "父级不能选择自己",
|
|
||||||
"ParentNull": "父级不存在 {0}",
|
|
||||||
"DeleteUserFirst": "请先删除职位下的用户"
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.SysOrg": {
|
"ThingsGateway.Admin.Application.SysOrg": {
|
||||||
"Category.Required": "{0} 是必填项",
|
"Category.Required": "{0} 是必填项",
|
||||||
"Name.Required": "{0} 是必填项",
|
"Name.Required": "{0} 是必填项",
|
||||||
"Code.Required": "{0} 是必填项",
|
"Code.Required": "{0} 是必填项",
|
||||||
"Category": "分类",
|
"Category": "分类",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
"Code": "代码",
|
"Code": "代码",
|
||||||
"Status": "状态",
|
"Status": "状态",
|
||||||
"ParentId": "上级机构",
|
"ParentId": "上级机构",
|
||||||
"Names": "机构全称",
|
"Names": "机构全称",
|
||||||
"Remark": "备注",
|
"Remark": "备注",
|
||||||
"DirectorId": "主管",
|
"DirectorId": "主管",
|
||||||
"SortCode": "排序",
|
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
|
||||||
"CreateTime": "创建时间",
|
"CodeDup": "存在重复的编码 {0}",
|
||||||
"UpdateTime": "更新时间",
|
"NameDup": "存在重复的名称 {0}",
|
||||||
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
|
"CanotContainsSelf": "不可包含自己",
|
||||||
"CodeDup": "存在重复的编码 {0}",
|
"TargetNameDup": "目标节点存在重复的名称 {0}",
|
||||||
"NameDup": "存在重复的名称 {0}",
|
"ParentChoiceSelf": "父级不能选择自己",
|
||||||
"CanotContainsSelf": "不可包含自己",
|
"ParentNull": "父级不存在 {0}",
|
||||||
"TargetNameDup": "目标节点存在重复的名称 {0}",
|
"DeleteUserFirst": "请先删除机构下的用户",
|
||||||
"ParentChoiceSelf": "父级不能选择自己",
|
"DeleteRoleFirst": "请先删除机构下的角色",
|
||||||
"ParentNull": "父级不存在 {0}",
|
"DeletePositionFirst": "请先删除机构下的职位",
|
||||||
"DeleteUserFirst": "请先删除机构下的用户",
|
"RootOrg": "无法创建顶层机构"
|
||||||
"DeleteRoleFirst": "请先删除机构下的角色",
|
},
|
||||||
"DeletePositionFirst": "请先删除机构下的职位",
|
"ThingsGateway.Admin.Application.OrgEnum": {
|
||||||
"RootOrg": "无法创建顶层机构"
|
"COMPANY": "公司",
|
||||||
},
|
"DEPT": "部门"
|
||||||
"ThingsGateway.Admin.Application.OrgEnum": {
|
},
|
||||||
"COMPANY": "公司",
|
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
|
||||||
"DEPT": "部门"
|
"HIGH": "高层",
|
||||||
},
|
"MIDDLE": "中层",
|
||||||
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
|
"LOW": "低层"
|
||||||
"HIGH": "高层",
|
},
|
||||||
"MIDDLE": "中层",
|
|
||||||
"LOW": "低层"
|
|
||||||
},
|
|
||||||
|
|
||||||
//controller
|
//controller
|
||||||
"ThingsGateway.Admin.Application.AuthController": {
|
"ThingsGateway.Admin.Application.AuthController": {
|
||||||
//auth
|
//auth
|
||||||
"AuthController": "登录API",
|
"AuthController": "登录API",
|
||||||
"LoginAsync": "登录",
|
"LoginAsync": "登录",
|
||||||
"LogoutAsync": "注销"
|
"LogoutAsync": "注销"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.TestController": {
|
"ThingsGateway.Admin.Application.TestController": {
|
||||||
//auth
|
//auth
|
||||||
"TestController": "测试API",
|
"TestController": "测试API",
|
||||||
"Test": "测试"
|
"Test": "测试"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||||
//auth
|
//auth
|
||||||
"OpenApiAuthController": "登录API",
|
"OpenApiAuthController": "登录API",
|
||||||
"LoginAsync": "登录",
|
"LoginAsync": "登录",
|
||||||
"LogoutAsync": "注销"
|
"LogoutAsync": "注销"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.FileService": {
|
"ThingsGateway.Admin.Application.FileService": {
|
||||||
"FileNullError": "文件不能为空",
|
"FileNullError": "文件不能为空",
|
||||||
"FileLengthError": "文件大小不允许超过 {0} M",
|
"FileLengthError": "文件大小不允许超过 {0} M",
|
||||||
"FileTypeError": "不支持 {0} 格式"
|
"FileTypeError": "不支持 {0} 格式"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
||||||
"TokenOver": "登录已过期,请重新登录",
|
"TokenOver": "登录已过期,请重新登录",
|
||||||
"NoPermission": "禁止访问,没有权限"
|
"NoPermission": "禁止访问,没有权限"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.AuthService": {
|
"ThingsGateway.Admin.Application.AuthService": {
|
||||||
"TenantNull": "租户不存在",
|
"TenantNull": "租户不存在",
|
||||||
"OrgDisable": "所属公司/部门已停用,请联系管理员",
|
"OrgDisable": "所属公司/部门已停用,请联系管理员",
|
||||||
"SingleLoginWarn": "您的账号已在别处登录",
|
"SingleLoginWarn": "您的账号已在别处登录",
|
||||||
"UserNull": "用户 {0} 不存在",
|
"UserNull": "用户 {0} 不存在",
|
||||||
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
|
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
|
||||||
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
|
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
|
||||||
"UserDisable": "账号 {0} 已停用",
|
"UserDisable": "账号 {0} 已停用",
|
||||||
"MustDesc": "密码需要DESC加密后传入",
|
"MustDesc": "密码需要DESC加密后传入",
|
||||||
"UserNoModule": "该账号未分配模块,请联系管理员"
|
"UserNoModule": "该账号未分配模块,请联系管理员"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||||
"Environment": "主机环境",
|
"Environment": "主机环境",
|
||||||
"FrameworkDescription": "NET框架",
|
"FrameworkDescription": "NET框架",
|
||||||
"OsArchitecture": "系统架构",
|
"OsArchitecture": "系统架构",
|
||||||
"UUID": "唯一编码",
|
"UUID": "唯一编码",
|
||||||
"UpdateTime": "更新时间"
|
"UpdateTime": "更新时间"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
|
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
|
||||||
"DriveUsage": "磁盘使用率",
|
"DriveUsage": "磁盘使用率",
|
||||||
"MemoryUsage": "内存",
|
"MemoryUsage": "内存",
|
||||||
"CpuUsage": "CPU使用率",
|
"CpuUsage": "CPU使用率",
|
||||||
"Temperature": "温度",
|
"Temperature": "温度",
|
||||||
"Battery": "电池"
|
"Battery": "电池"
|
||||||
},
|
},
|
||||||
|
|
||||||
//oper
|
//oper
|
||||||
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
||||||
//dict
|
//dict
|
||||||
"SaveDict": "修改字典",
|
"SaveDict": "修改字典",
|
||||||
"DeleteDict": "删除字典",
|
"DeleteDict": "删除字典",
|
||||||
"EditLoginPolicy": "修改登录策略",
|
"EditLoginPolicy": "修改登录策略",
|
||||||
"EditPasswordPolicy": "修改密码策略",
|
"EditPasswordPolicy": "修改密码策略",
|
||||||
"EditPagePolicy": "修改页面策略",
|
"EditPagePolicy": "修改页面策略",
|
||||||
"EditWebsitePolicy": "修改网站设置",
|
"EditWebsitePolicy": "修改网站设置",
|
||||||
//operlog
|
//operlog
|
||||||
"DeleteOperLog": "删除操作日志",
|
"DeleteOperLog": "删除操作日志",
|
||||||
"ExportOperLog": "导出操作日志",
|
"ExportOperLog": "导出操作日志",
|
||||||
|
|
||||||
//resource
|
//resource
|
||||||
"SaveResource": "修改资源",
|
"SaveResource": "修改资源",
|
||||||
"DeleteResource": "删除资源",
|
"DeleteResource": "删除资源",
|
||||||
|
|
||||||
//role
|
//role
|
||||||
"SaveRole": "修改角色",
|
"SaveRole": "修改角色",
|
||||||
"DeleteRole": "删除角色",
|
"DeleteRole": "删除角色",
|
||||||
"RoleGrantResource": "角色授权资源",
|
"RoleGrantResource": "角色授权资源",
|
||||||
"RoleGrantUser": "角色授权用户",
|
"RoleGrantUser": "角色授权用户",
|
||||||
"RoleGrantApiPermission": "角色授权OpenApi",
|
"RoleGrantApiPermission": "角色授权OpenApi",
|
||||||
"GrantApi": "API",
|
"GrantApi": "API",
|
||||||
"GrantUser": "用户",
|
"GrantUser": "用户",
|
||||||
"GrantRole": "角色",
|
"GrantRole": "角色",
|
||||||
"GrantResource": "资源",
|
"GrantResource": "资源",
|
||||||
//user
|
//user
|
||||||
"SaveUser": "修改用户",
|
"SaveUser": "修改用户",
|
||||||
"DeleteuSER": "删除用户",
|
"DeleteuSER": "删除用户",
|
||||||
"ResetPassword": "重置密码",
|
"ResetPassword": "重置密码",
|
||||||
"UserGrantRole": "用户授权角色",
|
"UserGrantRole": "用户授权角色",
|
||||||
"UserGrantResource": "用户授权资源",
|
"UserGrantResource": "用户授权资源",
|
||||||
"UserGrantApiPermission": "用户授权OpenApi",
|
"UserGrantApiPermission": "用户授权OpenApi",
|
||||||
|
|
||||||
//usercenter
|
//usercenter
|
||||||
"UpdateUserInfo": "更新个人信息",
|
"UpdateUserInfo": "更新个人信息",
|
||||||
"WorkbenchInfo": "更新个人工作台",
|
"WorkbenchInfo": "更新个人工作台",
|
||||||
"UpdatePassword": "更新个人密码",
|
"UpdatePassword": "更新个人密码",
|
||||||
|
|
||||||
//session
|
//session
|
||||||
"ExitVerificat": "强退令牌",
|
"ExitVerificat": "强退令牌",
|
||||||
"ExitSession": "强退会话",
|
"ExitSession": "强退会话",
|
||||||
|
|
||||||
|
|
||||||
"CopyOrg": "复制机构",
|
"CopyOrg": "复制机构",
|
||||||
"DeleteOrg": "删除机构",
|
"DeleteOrg": "删除机构",
|
||||||
"SaveOrg": "保存机构",
|
"SaveOrg": "保存机构",
|
||||||
|
|
||||||
"DeletePosition": "删除岗位",
|
"DeletePosition": "删除岗位",
|
||||||
"SavePosition": "保存岗位",
|
"SavePosition": "保存岗位",
|
||||||
|
|
||||||
"NoPermission": "无权限操作",
|
"NoPermission": "无权限操作",
|
||||||
|
|
||||||
"CopyResource": "复制资源",
|
"CopyResource": "复制资源",
|
||||||
"ChangeParentResource": "更改父节点"
|
"ChangeParentResource": "更改父节点"
|
||||||
},
|
},
|
||||||
|
|
||||||
//service
|
//service
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.HardwareJob": {
|
"ThingsGateway.Admin.Application.HardwareJob": {
|
||||||
"GetHardwareInfoFail": "获取硬件信息出错"
|
"GetHardwareInfoFail": "获取硬件信息出错"
|
||||||
},
|
},
|
||||||
|
|
||||||
//dto
|
//dto
|
||||||
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
||||||
"Account": "账号",
|
"Account": "账号",
|
||||||
"OrgId": "机构"
|
"OrgId": "机构"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
|
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
|
||||||
"Module": "模块",
|
"Module": "模块",
|
||||||
"Href": "路径",
|
"Href": "路径",
|
||||||
"Title": "标题"
|
"Title": "标题"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.WorkbenchInfo": {
|
"ThingsGateway.Admin.Application.WorkbenchInfo": {
|
||||||
"Razor": "主页",
|
"Razor": "主页",
|
||||||
"Shortcuts": "快捷方式"
|
"Shortcuts": "快捷方式"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
|
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
|
||||||
"Password": "密码",
|
"Password": "密码",
|
||||||
"NewPassword": "新密码",
|
"NewPassword": "新密码",
|
||||||
"ConfirmPassword": "确认密码",
|
"ConfirmPassword": "确认密码",
|
||||||
"Password.Required": " {0} 是必填项",
|
"Password.Required": " {0} 是必填项",
|
||||||
"NewPassword.Required": " {0} 是必填项",
|
"NewPassword.Required": " {0} 是必填项",
|
||||||
"ConfirmPassword.Required": " {0} 是必填项"
|
"ConfirmPassword.Required": " {0} 是必填项"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.VerificatInfo": {
|
"ThingsGateway.Admin.Application.VerificatInfo": {
|
||||||
"Expire": "过期时间(分)",
|
"Expire": "过期时间(分)",
|
||||||
"Online": "在线状态",
|
"Online": "在线状态",
|
||||||
"VerificatTimeout": "超时时间",
|
"VerificatTimeout": "超时时间",
|
||||||
"Device": "登录设备",
|
"Device": "登录设备",
|
||||||
"LoginIp": "登录IP",
|
"LoginIp": "登录IP",
|
||||||
"LoginTime": "登录时间"
|
"LoginTime": "登录时间"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.SessionOutput": {
|
"ThingsGateway.Admin.Application.SessionOutput": {
|
||||||
"Account": "账号",
|
"Account": "账号",
|
||||||
"Online": "在线状态",
|
"Online": "在线状态",
|
||||||
"LatestLoginIp": "最新登录ip",
|
"LatestLoginIp": "最新登录ip",
|
||||||
"LatestLoginTime": "最新登录时间",
|
"LatestLoginTime": "最新登录时间",
|
||||||
"VerificatCount": "令牌数量"
|
"VerificatCount": "令牌数量"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.SysDict": {
|
"ThingsGateway.Admin.Application.SysDict": {
|
||||||
"Category.Required": "{0} 是必填项",
|
"Category.Required": "{0} 是必填项",
|
||||||
"Name.Required": "{0} 是必填项",
|
"Name.Required": "{0} 是必填项",
|
||||||
"Code.Required": "{0} 是必填项",
|
"Code.Required": "{0} 是必填项",
|
||||||
"Category": "分类",
|
"Category": "分类",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
"Code": "代码",
|
"Code": "代码",
|
||||||
"Remark": "备注",
|
"Remark": "备注",
|
||||||
"SortCode": "排序",
|
"DictDup": "存在重复的配置 分类 {0} 名称 {1}",
|
||||||
"CreateTime": "创建时间",
|
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置"
|
||||||
"UpdateTime": "更新时间",
|
},
|
||||||
"DictDup": "存在重复的配置 分类 {0} 名称 {1}",
|
|
||||||
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置"
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.SysOperateLog": {
|
"ThingsGateway.Admin.Application.SysOperateLog": {
|
||||||
"ClassName": "类名",
|
"ClassName": "类名",
|
||||||
"ExeMessage": "具体消息",
|
"ExeMessage": "具体消息",
|
||||||
"MethodName": "方法名称",
|
"MethodName": "方法名称",
|
||||||
"ParamJson": "请求参数",
|
"ParamJson": "请求参数",
|
||||||
"ReqMethod": "请求方式",
|
"ReqMethod": "请求方式",
|
||||||
"ReqUrl": "请求地址",
|
"ReqUrl": "请求地址",
|
||||||
"ResultJson": "返回结果",
|
"ResultJson": "返回结果",
|
||||||
"Category": "日志分类",
|
"Category": "日志分类",
|
||||||
"ExeStatus": "执行状态",
|
"ExeStatus": "执行状态",
|
||||||
"Name": "日志名称",
|
"Name": "日志名称",
|
||||||
"OpAccount": "账号",
|
"OpAccount": "账号",
|
||||||
"OpBrowser": "浏览器",
|
"OpBrowser": "浏览器",
|
||||||
"OpIp": "ip",
|
"OpIp": "ip",
|
||||||
"OpOs": "系统",
|
"OpOs": "系统",
|
||||||
"OpTime": "操作时间",
|
"OpTime": "操作时间",
|
||||||
"VerificatId": "验证Id"
|
"VerificatId": "验证Id"
|
||||||
|
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.OperateLogPageInput": {
|
"ThingsGateway.Admin.Application.OperateLogPageInput": {
|
||||||
"SearchDate": "时间范围",
|
"SearchDate": "时间范围",
|
||||||
"Account": "操作账号",
|
"Account": "操作账号",
|
||||||
"Category": "分类"
|
"Category": "分类"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.LoginInput": {
|
"ThingsGateway.Admin.Application.LoginInput": {
|
||||||
"Account": "登录账号",
|
"Account": "登录账号",
|
||||||
"Password": "登录密码",
|
"Password": "登录密码",
|
||||||
"Account.Required": "{0} 是必填项",
|
"Account.Required": "{0} 是必填项",
|
||||||
"Password.Required": "{0} 是必填项"
|
"Password.Required": "{0} 是必填项"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.LogoutInput": {
|
"ThingsGateway.Admin.Application.LogoutInput": {
|
||||||
"VerificatId.Required": "{0} 是必填项"
|
"VerificatId.Required": "{0} 是必填项"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.AppConfig": {
|
"ThingsGateway.Admin.Application.AppConfig": {
|
||||||
"LoginPolicy": "登录策略",
|
"LoginPolicy": "登录策略",
|
||||||
"PasswordPolicy": "密码策略",
|
"PasswordPolicy": "密码策略",
|
||||||
"PagePolicy": "页面设置",
|
"PagePolicy": "页面设置",
|
||||||
"WebsitePolicy": "网站设置"
|
"WebsitePolicy": "网站设置"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.LoginPolicy": {
|
"ThingsGateway.Admin.Application.LoginPolicy": {
|
||||||
"SingleOpen": "单用户登录开关",
|
"SingleOpen": "单用户登录开关",
|
||||||
"ErrorLockTime": "登录错误锁定时长(分)",
|
"ErrorLockTime": "登录错误锁定时长(分)",
|
||||||
"ErrorResetTime": "登录错误次数过期时长(分)",
|
"ErrorResetTime": "登录错误次数过期时长(分)",
|
||||||
"ErrorCount": "登录错误次数锁定阈值",
|
"ErrorCount": "登录错误次数锁定阈值",
|
||||||
"VerificatExpireTime": "登录过期时间(分)",
|
"VerificatExpireTime": "登录过期时间(分)",
|
||||||
"ErrorLockTime.MinValue": " {0} 值太小",
|
"ErrorLockTime.MinValue": " {0} 值太小",
|
||||||
"ErrorResetTime.MinValue": " {0} 值太小",
|
"ErrorResetTime.MinValue": " {0} 值太小",
|
||||||
"ErrorCount.MinValue": " {0} 值太小",
|
"ErrorCount.MinValue": " {0} 值太小",
|
||||||
"VerificatExpireTime.MinValue": " {0} 值太小"
|
"VerificatExpireTime.MinValue": " {0} 值太小"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.PagePolicy": {
|
"ThingsGateway.Admin.Application.PagePolicy": {
|
||||||
"Shortcuts": "默认快捷方式",
|
"Shortcuts": "默认快捷方式",
|
||||||
"Razor": "默认主页"
|
"Razor": "默认主页"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.PasswordPolicy": {
|
"ThingsGateway.Admin.Application.PasswordPolicy": {
|
||||||
"DefaultPassword": "默认用户密码",
|
"DefaultPassword": "默认用户密码",
|
||||||
"DefaultPassword.Required": " {0} 是必填项",
|
"DefaultPassword.Required": " {0} 是必填项",
|
||||||
"PasswordMinLen": "密码最小长度",
|
"PasswordMinLen": "密码最小长度",
|
||||||
"PasswordMinLen.MinValue": " {0} 值太小",
|
"PasswordMinLen.MinValue": " {0} 值太小",
|
||||||
"PasswordContainNum": "包含数字",
|
"PasswordContainNum": "包含数字",
|
||||||
"PasswordContainLower": "包含小写字母",
|
"PasswordContainLower": "包含小写字母",
|
||||||
"PasswordContainUpper": "包含大写字母",
|
"PasswordContainUpper": "包含大写字母",
|
||||||
"PasswordContainChar": "包含特殊字符"
|
"PasswordContainChar": "包含特殊字符"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.WebsitePolicy": {
|
"ThingsGateway.Admin.Application.WebsitePolicy": {
|
||||||
"WebStatus": "是否开放",
|
"WebStatus": "是否开放",
|
||||||
"CloseTip": "关闭提示",
|
"CloseTip": "关闭提示",
|
||||||
"CloseTip.Required": " {0} 是必填项"
|
"CloseTip.Required": " {0} 是必填项"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//enum
|
//enum
|
||||||
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
|
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
|
||||||
"Module": "模块",
|
"Module": "模块",
|
||||||
"Menu": "菜单",
|
"Menu": "菜单",
|
||||||
"Button": "按钮"
|
"Button": "按钮"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.TargetEnum": {
|
"ThingsGateway.Admin.Application.TargetEnum": {
|
||||||
"_self": "本窗口",
|
"_self": "本窗口",
|
||||||
"_blank": "新窗口",
|
"_blank": "新窗口",
|
||||||
"_parent": "父级窗口",
|
"_parent": "父级窗口",
|
||||||
"_top": "顶级窗口"
|
"_top": "顶级窗口"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.DictTypeEnum": {
|
"ThingsGateway.Admin.Application.DictTypeEnum": {
|
||||||
"System": "系统配置",
|
"System": "系统配置",
|
||||||
"Define": "业务配置"
|
"Define": "业务配置"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
|
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
|
||||||
"Login": "登录",
|
"Login": "登录",
|
||||||
"Logout": "注销",
|
"Logout": "注销",
|
||||||
"Operate": "操作",
|
"Operate": "操作",
|
||||||
"Exception": "异常"
|
"Exception": "异常"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Admin.Application.LogEnum": {
|
"ThingsGateway.Admin.Application.LogEnum": {
|
||||||
"SUCCESS": "成功",
|
"SUCCESS": "成功",
|
||||||
"FAIL": "失败"
|
"FAIL": "失败"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway.Extension;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.FriendlyException;
|
using ThingsGateway.FriendlyException;
|
||||||
@@ -18,8 +19,6 @@ using ThingsGateway.Logging;
|
|||||||
using ThingsGateway.NewLife.Json.Extension;
|
using ThingsGateway.NewLife.Json.Extension;
|
||||||
using ThingsGateway.Razor;
|
using ThingsGateway.Razor;
|
||||||
|
|
||||||
using UAParser;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,33 +40,36 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
|||||||
/// <param name="flush"></param>
|
/// <param name="flush"></param>
|
||||||
public async Task WriteAsync(LogMessage logMsg, bool flush)
|
public async Task WriteAsync(LogMessage logMsg, bool flush)
|
||||||
{
|
{
|
||||||
//获取请求json字符串
|
|
||||||
var jsonString = logMsg.Context.Get("loggingMonitor").ToString();
|
|
||||||
//转成实体
|
//转成实体
|
||||||
var loggingMonitor = jsonString.FromJsonNetString<LoggingMonitorJson>();
|
var requestAuditData = logMsg.Context.Get(nameof(RequestAuditData)) as RequestAuditData;
|
||||||
//日志时间赋值
|
//日志时间赋值
|
||||||
loggingMonitor.LogDateTime = logMsg.LogDateTime;
|
requestAuditData.LogDateTime = logMsg.LogDateTime;
|
||||||
// loggingMonitor.ReturnInformation.Value
|
// requestAuditData.ReturnInformation.Value
|
||||||
//验证失败不记录日志
|
//验证失败不记录日志
|
||||||
bool save = false;
|
bool save = false;
|
||||||
if (loggingMonitor.Validation == null)
|
if (requestAuditData.Validation == null)
|
||||||
{
|
{
|
||||||
var operation = logMsg.Context.Get(LoggingConst.Operation).ToString();//获取操作名称
|
var operation = requestAuditData.Operation;//获取操作名称
|
||||||
var client = (ClientInfo)logMsg.Context.Get(LoggingConst.Client);//获取客户端信息
|
var client = requestAuditData.Client;//获取客户端信息
|
||||||
var path = logMsg.Context.Get(LoggingConst.Path).ToString();//获取操作名称
|
var path = requestAuditData.Path;//获取操作名称
|
||||||
var method = logMsg.Context.Get(LoggingConst.Method).ToString();//获取方法
|
var method = requestAuditData.Method;//获取方法
|
||||||
|
var methodInfo = requestAuditData.MethodInfo;
|
||||||
|
var login = methodInfo.GetCustomAttribute(typeof(LoginLogAttribute));
|
||||||
|
var logout = methodInfo.GetCustomAttribute(typeof(LogoutLogAttribute));
|
||||||
|
|
||||||
//表示访问日志
|
//表示访问日志
|
||||||
if (path == "/api/auth/login" || path == "/api/auth/logout")
|
if (login != null || logout != null)
|
||||||
{
|
{
|
||||||
//如果没有异常信息
|
//如果没有异常信息
|
||||||
if (loggingMonitor.Exception == null)
|
if (requestAuditData.Exception == null)
|
||||||
{
|
{
|
||||||
save = await CreateVisitLog(operation, path, loggingMonitor, client, flush).ConfigureAwait(false);//添加到访问日志
|
LogCateGoryEnum logCateGoryEnum = login != null ? LogCateGoryEnum.Login : LogCateGoryEnum.Logout;
|
||||||
|
save = await CreateVisitLog(operation, path, requestAuditData, client, logCateGoryEnum, flush).ConfigureAwait(false);//添加到访问日志
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//添加到异常日志
|
//添加到异常日志
|
||||||
save = await CreateOperationLog(operation, path, loggingMonitor, client, flush).ConfigureAwait(false);
|
save = await CreateOperationLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -76,7 +78,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
|||||||
if (!operation.IsNullOrWhiteSpace() && method == "POST")
|
if (!operation.IsNullOrWhiteSpace() && method == "POST")
|
||||||
{
|
{
|
||||||
//添加到操作日志
|
//添加到操作日志
|
||||||
save = await CreateOperationLog(operation, path, loggingMonitor, client, flush).ConfigureAwait(false);
|
save = await CreateOperationLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,27 +93,21 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="operation">操作名称</param>
|
/// <param name="operation">操作名称</param>
|
||||||
/// <param name="path">请求地址</param>
|
/// <param name="path">请求地址</param>
|
||||||
/// <param name="loggingMonitor">loggingMonitor</param>
|
/// <param name="requestAuditData">requestAuditData</param>
|
||||||
/// <param name="clientInfo">客户端信息</param>
|
/// <param name="userAgent">客户端信息</param>
|
||||||
/// <param name="flush"></param>
|
/// <param name="flush"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<bool> CreateOperationLog(string operation, string path, LoggingMonitorJson loggingMonitor, ClientInfo clientInfo, bool flush)
|
private async Task<bool> CreateOperationLog(string operation, string path, RequestAuditData requestAuditData, UserAgent userAgent, bool flush)
|
||||||
{
|
{
|
||||||
//账号
|
//账号
|
||||||
var opAccount = loggingMonitor.AuthorizationClaims?.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault();
|
var opAccount = requestAuditData.AuthorizationClaims?.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault();
|
||||||
|
|
||||||
//获取参数json字符串,
|
//获取参数json字符串,
|
||||||
var paramJson = loggingMonitor.Parameters == null || loggingMonitor.Parameters.Count == 0 ? null : loggingMonitor.Parameters[0].Value.ToJsonNetString();
|
var paramJson = requestAuditData.Parameters == null || requestAuditData.Parameters.Count == 0 ? null : requestAuditData.Parameters.ToSystemTextJsonString();
|
||||||
|
|
||||||
//获取结果json字符串
|
//获取结果json字符串
|
||||||
var resultJson = string.Empty;
|
var resultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString();
|
||||||
if (loggingMonitor.ReturnInformation != null)//如果有返回值
|
|
||||||
{
|
|
||||||
if (loggingMonitor.ReturnInformation.Value != null)//如果返回值不为空
|
|
||||||
{
|
|
||||||
resultJson = loggingMonitor.ReturnInformation.Value.ToJsonNetString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//操作日志表实体
|
//操作日志表实体
|
||||||
var sysLogOperate = new SysOperateLog
|
var sysLogOperate = new SysOperateLog
|
||||||
@@ -119,29 +115,29 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
|||||||
Name = operation,
|
Name = operation,
|
||||||
Category = LogCateGoryEnum.Operate,
|
Category = LogCateGoryEnum.Operate,
|
||||||
ExeStatus = true,
|
ExeStatus = true,
|
||||||
OpIp = loggingMonitor.RemoteIPv4,
|
OpIp = requestAuditData.RemoteIPv4,
|
||||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
OpBrowser = userAgent?.Browser,
|
||||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
OpOs = userAgent?.Platform,
|
||||||
OpTime = loggingMonitor.LogDateTime.LocalDateTime,
|
OpTime = requestAuditData.LogDateTime.LocalDateTime,
|
||||||
OpAccount = opAccount,
|
OpAccount = opAccount,
|
||||||
ReqMethod = loggingMonitor.HttpMethod,
|
ReqMethod = requestAuditData.Method,
|
||||||
ReqUrl = path,
|
ReqUrl = path,
|
||||||
ResultJson = resultJson,
|
ResultJson = resultJson,
|
||||||
ClassName = loggingMonitor.DisplayName,
|
ClassName = requestAuditData.ControllerName,
|
||||||
MethodName = loggingMonitor.ActionName,
|
MethodName = requestAuditData.ActionName,
|
||||||
ParamJson = paramJson,
|
ParamJson = paramJson,
|
||||||
VerificatId = UserManager.VerificatId,
|
VerificatId = UserManager.VerificatId,
|
||||||
};
|
};
|
||||||
//如果异常不为空
|
//如果异常不为空
|
||||||
if (loggingMonitor.Exception != null)
|
if (requestAuditData.Exception != null)
|
||||||
{
|
{
|
||||||
sysLogOperate.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
sysLogOperate.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
||||||
sysLogOperate.ExeStatus = false;//操作状态为失败
|
sysLogOperate.ExeStatus = false;//操作状态为失败
|
||||||
|
|
||||||
if (loggingMonitor.Exception.Type == typeof(AppFriendlyException).ToString())
|
if (requestAuditData.Exception.Type == typeof(AppFriendlyException).ToString())
|
||||||
sysLogOperate.ExeMessage = loggingMonitor?.Exception.Message;
|
sysLogOperate.ExeMessage = requestAuditData?.Exception.Message;
|
||||||
else
|
else
|
||||||
sysLogOperate.ExeMessage = $"{loggingMonitor.Exception.Type}:{loggingMonitor.Exception.Message}{Environment.NewLine}{loggingMonitor.Exception.StackTrace}";
|
sysLogOperate.ExeMessage = $"{requestAuditData.Exception.Type}:{requestAuditData.Exception.Message}{Environment.NewLine}{requestAuditData.Exception.StackTrace}";
|
||||||
}
|
}
|
||||||
|
|
||||||
_operateLogMessageQueue.Enqueue(sysLogOperate);
|
_operateLogMessageQueue.Enqueue(sysLogOperate);
|
||||||
@@ -160,46 +156,48 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="operation">访问类型</param>
|
/// <param name="operation">访问类型</param>
|
||||||
/// <param name="path"></param>
|
/// <param name="path"></param>
|
||||||
/// <param name="loggingMonitor">loggingMonitor</param>
|
/// <param name="requestAuditData">requestAuditData</param>
|
||||||
/// <param name="clientInfo">客户端信息</param>
|
/// <param name="userAgent">客户端信息</param>
|
||||||
|
/// <param name="logCateGoryEnum">logCateGory</param>
|
||||||
/// <param name="flush"></param>
|
/// <param name="flush"></param>
|
||||||
private async Task<bool> CreateVisitLog(string operation, string path, LoggingMonitorJson loggingMonitor, ClientInfo clientInfo, bool flush)
|
private async Task<bool> CreateVisitLog(string operation, string path, RequestAuditData requestAuditData, UserAgent userAgent, LogCateGoryEnum logCateGoryEnum, bool flush)
|
||||||
{
|
{
|
||||||
long verificatId = 0;//验证Id
|
long verificatId = 0;//验证Id
|
||||||
var opAccount = "";//用户账号
|
var opAccount = "";//用户账号
|
||||||
if (path == "/api/auth/login")
|
if (logCateGoryEnum == LogCateGoryEnum.Login)
|
||||||
{
|
{
|
||||||
//如果是登录,用户信息就从返回值里拿
|
//如果是登录,用户信息就从返回值里拿
|
||||||
var result = loggingMonitor.ReturnInformation?.Value?.ToJsonNetString();//返回值转json
|
if (requestAuditData.ReturnInformation is UnifyResult<LoginOutput> userInfo)
|
||||||
var userInfo = result.FromJsonNetString<UnifyResult<LoginOutput>>();//格式化成user表
|
{
|
||||||
opAccount = userInfo.Data.Account;//赋值账号
|
opAccount = userInfo.Data.Account;//赋值账号
|
||||||
verificatId = userInfo.Data.VerificatId;
|
verificatId = userInfo.Data.VerificatId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//如果是登录出,用户信息就从AuthorizationClaims里拿
|
//如果是登录出,用户信息就从AuthorizationClaims里拿
|
||||||
opAccount = loggingMonitor.AuthorizationClaims.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault();
|
opAccount = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault();
|
||||||
verificatId = loggingMonitor.AuthorizationClaims.Where(it => it.Type == ClaimConst.VerificatId).Select(it => it.Value).FirstOrDefault().ToLong();
|
verificatId = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.VerificatId).Select(it => it.Value).FirstOrDefault().ToLong();
|
||||||
}
|
}
|
||||||
//日志表实体
|
//日志表实体
|
||||||
var sysLogVisit = new SysOperateLog
|
var sysLogVisit = new SysOperateLog
|
||||||
{
|
{
|
||||||
Name = operation,
|
Name = operation,
|
||||||
Category = path == "/api/auth/login" ? LogCateGoryEnum.Login : LogCateGoryEnum.Logout,
|
Category = logCateGoryEnum,
|
||||||
ExeStatus = true,
|
ExeStatus = true,
|
||||||
OpIp = loggingMonitor.RemoteIPv4,
|
OpIp = requestAuditData.RemoteIPv4,
|
||||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
OpBrowser = userAgent?.Browser,
|
||||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
OpOs = userAgent?.Platform,
|
||||||
OpTime = loggingMonitor.LogDateTime.LocalDateTime,
|
OpTime = requestAuditData.LogDateTime.LocalDateTime,
|
||||||
VerificatId = verificatId,
|
VerificatId = verificatId,
|
||||||
OpAccount = opAccount,
|
OpAccount = opAccount,
|
||||||
|
|
||||||
ReqMethod = loggingMonitor.HttpMethod,
|
ReqMethod = requestAuditData.Method,
|
||||||
ReqUrl = path,
|
ReqUrl = path,
|
||||||
ResultJson = loggingMonitor.ReturnInformation?.Value?.ToJsonNetString(),
|
ResultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString(),
|
||||||
ClassName = loggingMonitor.DisplayName,
|
ClassName = requestAuditData.ControllerName,
|
||||||
MethodName = loggingMonitor.ActionName,
|
MethodName = requestAuditData.ActionName,
|
||||||
ParamJson = loggingMonitor.Parameters?.ToJsonNetString(),
|
ParamJson = requestAuditData.Parameters?.ToSystemTextJsonString(),
|
||||||
};
|
};
|
||||||
_operateLogMessageQueue.Enqueue(sysLogVisit);
|
_operateLogMessageQueue.Enqueue(sysLogVisit);
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 日志常量
|
/// 日志常量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LoggingConst
|
public static class LoggingConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分类
|
/// 分类
|
||||||
|
@@ -87,7 +87,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler
|
|||||||
var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false);
|
var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false);
|
||||||
|
|
||||||
//这里鉴别用户使能状态
|
//这里鉴别用户使能状态
|
||||||
if (user == null || !user.Status)
|
if (user?.Status != true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//这里鉴别用户使能状态
|
//这里鉴别用户使能状态
|
||||||
if (user == null || !user.Status)
|
if (user?.Status != true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -22,26 +22,19 @@ using System.Globalization;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway.Extension;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.SpecificationDocument;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
internal sealed class ApiPermissionService : IApiPermissionService
|
internal sealed class ApiPermissionService : IApiPermissionService
|
||||||
{
|
{
|
||||||
private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
|
private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
|
||||||
private readonly SwaggerGeneratorOptions _generatorOptions;
|
|
||||||
|
|
||||||
public ApiPermissionService(
|
public ApiPermissionService(
|
||||||
IOptions<SwaggerGeneratorOptions> generatorOptions,
|
IOptions<SwaggerGeneratorOptions> generatorOptions,
|
||||||
IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider)
|
IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider)
|
||||||
{
|
{
|
||||||
_generatorOptions = generatorOptions.Value;
|
|
||||||
_apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
|
_apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
|
||||||
}
|
}
|
||||||
private IEnumerable<string> GetDocumentNames()
|
|
||||||
{
|
|
||||||
return _generatorOptions.SwaggerDocs.Keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector()
|
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector()
|
||||||
@@ -53,37 +46,37 @@ internal sealed class ApiPermissionService : IApiPermissionService
|
|||||||
permissions = new();
|
permissions = new();
|
||||||
|
|
||||||
Dictionary<string, OpenApiPermissionTreeSelector> groupOpenApis = new();
|
Dictionary<string, OpenApiPermissionTreeSelector> groupOpenApis = new();
|
||||||
foreach (var item in GetDocumentNames())
|
|
||||||
{
|
|
||||||
OpenApiPermissionTreeSelector openApiPermissionTreeSelector = new() { ApiName = item ?? "Default" };
|
|
||||||
groupOpenApis.TryAdd(openApiPermissionTreeSelector.ApiName, openApiPermissionTreeSelector);
|
|
||||||
}
|
|
||||||
var apiDescriptions = _apiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items;
|
var apiDescriptions = _apiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items;
|
||||||
|
|
||||||
|
foreach (var item1 in apiDescriptions)
|
||||||
|
{
|
||||||
|
foreach (var item in item1.Items)
|
||||||
|
{
|
||||||
|
if (item.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||||
|
{
|
||||||
|
OpenApiPermissionTreeSelector openApiPermissionTreeSelector = new() { ApiName = controllerActionDescriptor.ControllerName ?? "Default" };
|
||||||
|
groupOpenApis.TryAdd(openApiPermissionTreeSelector.ApiName, openApiPermissionTreeSelector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取所有需要数据权限的控制器
|
// 获取所有需要数据权限的控制器
|
||||||
var controllerTypes =
|
var controllerTypes =
|
||||||
App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(RolePermissionAttribute), false));
|
App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(RolePermissionAttribute), false));
|
||||||
|
|
||||||
foreach (var groupOpenApi in groupOpenApis)
|
//foreach (var groupOpenApi in groupOpenApis)
|
||||||
{
|
{
|
||||||
|
|
||||||
foreach (var apiDescriptionGroup in apiDescriptions)
|
foreach (var apiDescriptionGroup in apiDescriptions)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
var routes = apiDescriptionGroup.Items.Where(api => api.ActionDescriptor is ControllerActionDescriptor);
|
var routes = apiDescriptionGroup.Items.Where(api => api.ActionDescriptor is ControllerActionDescriptor);
|
||||||
|
|
||||||
OpenApiPermissionTreeSelector openApiPermissionTreeSelector = groupOpenApi.Value;
|
|
||||||
|
|
||||||
Dictionary<string, OpenApiPermissionTreeSelector> openApiPermissionTreeSelectorDict = new();
|
Dictionary<string, OpenApiPermissionTreeSelector> openApiPermissionTreeSelectorDict = new();
|
||||||
|
|
||||||
foreach (var route in routes)
|
foreach (var route in routes)
|
||||||
{
|
{
|
||||||
if (!SpecificationDocumentBuilder.CheckApiDescriptionInCurrentGroup(groupOpenApi.Key, route))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var actionDesc = (ControllerActionDescriptor)route.ActionDescriptor;
|
var actionDesc = (ControllerActionDescriptor)route.ActionDescriptor;
|
||||||
if (!actionDesc.ControllerTypeInfo.CustomAttributes.Any(a => a.AttributeType == typeof(RolePermissionAttribute)))
|
if (!actionDesc.ControllerTypeInfo.CustomAttributes.Any(a => a.AttributeType == typeof(RolePermissionAttribute)))
|
||||||
continue;
|
continue;
|
||||||
@@ -116,10 +109,8 @@ internal sealed class ApiPermissionService : IApiPermissionService
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
openApiPermissionTreeSelector.Children.AddRange(openApiPermissionTreeSelectorDict.Values);
|
if (openApiPermissionTreeSelectorDict.Values.Any(a => a.Children.Count > 0))
|
||||||
|
permissions.AddRange(openApiPermissionTreeSelectorDict.Values);
|
||||||
if (openApiPermissionTreeSelector.Children.Any(a => a.Children.Count > 0))
|
|
||||||
permissions.Add(openApiPermissionTreeSelector);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,12 +15,17 @@ using Microsoft.AspNetCore.WebUtilities;
|
|||||||
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
using UAParser;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
public class AppService : IAppService
|
public class AppService : IAppService
|
||||||
{
|
{
|
||||||
|
private readonly IUserAgentService UserAgentService;
|
||||||
|
private readonly IClaimsPrincipalService ClaimsPrincipalService;
|
||||||
|
public AppService(IUserAgentService userAgentService, IClaimsPrincipalService claimsPrincipalService)
|
||||||
|
{
|
||||||
|
UserAgentService = userAgentService;
|
||||||
|
ClaimsPrincipalService = claimsPrincipalService;
|
||||||
|
}
|
||||||
public string GetReturnUrl(string returnUrl)
|
public string GetReturnUrl(string returnUrl)
|
||||||
{
|
{
|
||||||
var url = QueryHelpers.AddQueryString(CookieAuthenticationDefaults.LoginPath, new Dictionary<string, string?>
|
var url = QueryHelpers.AddQueryString(CookieAuthenticationDefaults.LoginPath, new Dictionary<string, string?>
|
||||||
@@ -41,18 +46,16 @@ public class AppService : IAppService
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Parser Parser = Parser.GetDefault();
|
public UserAgent? UserAgent
|
||||||
public ClientInfo? ClientInfo
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var str = App.HttpContext?.Request?.Headers?.UserAgent;
|
var str = App.HttpContext?.Request?.Headers?.UserAgent;
|
||||||
ClientInfo? clientInfo = null;
|
|
||||||
if (!string.IsNullOrEmpty(str))
|
if (!string.IsNullOrEmpty(str))
|
||||||
{
|
{
|
||||||
clientInfo = Parser.Parse(str);
|
return UserAgentService.Parse(str);
|
||||||
}
|
}
|
||||||
return clientInfo;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ public class AppService : IAppService
|
|||||||
ExpiresUtc = diffTime,
|
ExpiresUtc = diffTime,
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
public ClaimsPrincipal? User => App.User;
|
public ClaimsPrincipal? User => ClaimsPrincipalService.User;
|
||||||
|
|
||||||
public string? RemoteIpAddress => App.HttpContext?.GetRemoteIpAddressToIPv4();
|
public string? RemoteIpAddress => App.HttpContext?.GetRemoteIpAddressToIPv4();
|
||||||
|
|
||||||
|
@@ -13,19 +13,17 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
using UAParser;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
public class HybridAppService : IAppService
|
public class HybridAppService : IAppService
|
||||||
{
|
{
|
||||||
public HybridAppService()
|
public HybridAppService(IUserAgentService userAgentService)
|
||||||
{
|
{
|
||||||
var str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0";
|
var str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0";
|
||||||
ClientInfo = Parser.GetDefault().Parse(str);
|
UserAgent = userAgentService.Parse(str);
|
||||||
RemoteIpAddress = "127.0.0.1";
|
RemoteIpAddress = "127.0.0.1";
|
||||||
}
|
}
|
||||||
public ClientInfo? ClientInfo { get; }
|
public UserAgent? UserAgent { get; }
|
||||||
|
|
||||||
private static BlazorHybridAuthenticationStateProvider _authenticationStateProvider;
|
private static BlazorHybridAuthenticationStateProvider _authenticationStateProvider;
|
||||||
private static BlazorHybridAuthenticationStateProvider AuthenticationStateProvider
|
private static BlazorHybridAuthenticationStateProvider AuthenticationStateProvider
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public class HybridClaimsPrincipalService : IClaimsPrincipalService
|
||||||
|
{
|
||||||
|
HybridAppService _hybridAppService;
|
||||||
|
public HybridClaimsPrincipalService(HybridAppService hybridAppService)
|
||||||
|
{
|
||||||
|
_hybridAppService = hybridAppService;
|
||||||
|
}
|
||||||
|
public ClaimsPrincipal? User => _hybridAppService.User;
|
||||||
|
|
||||||
|
}
|
@@ -11,8 +11,6 @@
|
|||||||
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
using UAParser;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
public interface IAppService
|
public interface IAppService
|
||||||
@@ -20,7 +18,7 @@ public interface IAppService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ClientInfo
|
/// ClientInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ClientInfo? ClientInfo { get; }
|
public UserAgent? UserAgent { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ClaimsPrincipal
|
/// ClaimsPrincipal
|
||||||
|
@@ -12,8 +12,6 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
using SqlSugar;
|
|
||||||
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
||||||
using ThingsGateway.DataEncryption;
|
using ThingsGateway.DataEncryption;
|
||||||
@@ -64,6 +62,10 @@ public class AuthService : IAuthService
|
|||||||
{
|
{
|
||||||
throw Oops.Bah(appConfig.WebsitePolicy.CloseTip);
|
throw Oops.Bah(appConfig.WebsitePolicy.CloseTip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
string? password = input.Password;
|
string? password = input.Password;
|
||||||
if (isCookie) //openApi登录不再需要解密
|
if (isCookie) //openApi登录不再需要解密
|
||||||
{
|
{
|
||||||
@@ -96,9 +98,9 @@ public class AuthService : IAuthService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task LoginOutAsync()
|
public async Task LoginOutAsync()
|
||||||
{
|
{
|
||||||
if (UserManager.UserId == 0)
|
if (UserManager.VerificatId == 0)
|
||||||
return;
|
return;
|
||||||
var verificatId = UserManager.UserId;
|
var verificatId = UserManager.VerificatId;
|
||||||
//获取用户信息
|
//获取用户信息
|
||||||
var userinfo = await _sysUserService.GetUserByAccountAsync(UserManager.UserAccount, UserManager.TenantId).ConfigureAwait(false);
|
var userinfo = await _sysUserService.GetUserByAccountAsync(UserManager.UserAccount, UserManager.TenantId).ConfigureAwait(false);
|
||||||
if (userinfo != null)
|
if (userinfo != null)
|
||||||
@@ -237,25 +239,20 @@ public class AuthService : IAuthService
|
|||||||
var logingEvent = new LoginEvent
|
var logingEvent = new LoginEvent
|
||||||
{
|
{
|
||||||
Ip = _appService.RemoteIpAddress,
|
Ip = _appService.RemoteIpAddress,
|
||||||
Device = App.GetService<IAppService>().ClientInfo?.OS?.ToString(),
|
Device = _appService.UserAgent?.Platform,
|
||||||
Expire = expire,
|
Expire = expire,
|
||||||
SysUser = sysUser,
|
SysUser = sysUser,
|
||||||
VerificatId = verificatId
|
VerificatId = verificatId
|
||||||
};
|
};
|
||||||
await WriteTokenToCache(loginPolicy, logingEvent).ConfigureAwait(false);//写入verificat到cache
|
await WriteTokenToCache(loginPolicy, logingEvent).ConfigureAwait(false);//写入verificat到cache
|
||||||
await UpdateUser(logingEvent).ConfigureAwait(false);
|
await UpdateUser(logingEvent).ConfigureAwait(false);
|
||||||
if (sysUser.Account == RoleConst.SuperAdmin)
|
|
||||||
{
|
|
||||||
var modules = (await _sysResourceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.Category == ResourceCategoryEnum.Module).OrderBy(a => a.SortCode);//获取模块列表
|
|
||||||
sysUser.ModuleList = modules.ToList();//模块列表赋值给用户
|
|
||||||
}
|
|
||||||
//返回结果
|
//返回结果
|
||||||
return new LoginOutput
|
return new LoginOutput
|
||||||
{
|
{
|
||||||
VerificatId = verificatId,
|
VerificatId = verificatId,
|
||||||
Account = sysUser.Account,
|
Account = sysUser.Account,
|
||||||
Id = sysUser.Id,
|
Id = sysUser.Id,
|
||||||
ModuleList = sysUser.ModuleList,
|
|
||||||
AccessToken = accessToken,
|
AccessToken = accessToken,
|
||||||
RefreshToken = refreshToken
|
RefreshToken = refreshToken
|
||||||
};
|
};
|
||||||
|
@@ -77,7 +77,7 @@ internal sealed class SysDictService : BaseService<SysDict>, ISysDictService
|
|||||||
//更新数据
|
//更新数据
|
||||||
List<SysDict> dicts = new List<SysDict>()
|
List<SysDict> dicts = new List<SysDict>()
|
||||||
{
|
{
|
||||||
new SysDict() { DictType = DictTypeEnum.System, Category = nameof(PagePolicy), Name = nameof(PagePolicy.Shortcuts), Code = input.Shortcuts.ToJsonNetString() },
|
new SysDict() { DictType = DictTypeEnum.System, Category = nameof(PagePolicy), Name = nameof(PagePolicy.Shortcuts), Code = input.Shortcuts.ToSystemTextJsonString() },
|
||||||
};
|
};
|
||||||
var storageable = await db.Storageable(dicts).WhereColumns(it => new { it.DictType, it.Category, it.Name }).ToStorageAsync().ConfigureAwait(false);
|
var storageable = await db.Storageable(dicts).WhereColumns(it => new { it.DictType, it.Category, it.Name }).ToStorageAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
@@ -16,9 +16,9 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// 内存推送事件服务
|
/// 内存推送事件服务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TEntry"></typeparam>
|
/// <typeparam name="TEntry"></typeparam>
|
||||||
public class EventService<TEntry> : IEventService<TEntry>
|
public class EventService<TEntry> : IEventService<TEntry>, IDisposable
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<string, Func<TEntry, Task>> Cache { get; } = new();
|
private ConcurrentDictionary<string, Func<TEntry, Task>> Cache = new();
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
@@ -11,8 +11,6 @@
|
|||||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
using Yitter.IdGenerator;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -28,7 +26,7 @@ public class UserIdProvider : IUserIdProvider
|
|||||||
|
|
||||||
if (UserId > 0)
|
if (UserId > 0)
|
||||||
{
|
{
|
||||||
return $"{UserId}{SysHub.Separate}{YitIdHelper.NextId()}";//返回用户ID
|
return $"{UserId}{SysHub.Separate}{CommonUtils.GetSingleId()}";//返回用户ID
|
||||||
}
|
}
|
||||||
|
|
||||||
return connection.ConnectionId;
|
return connection.ConnectionId;
|
||||||
|
@@ -334,7 +334,7 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
|
|||||||
flatList.Add(node);
|
flatList.Add(node);
|
||||||
|
|
||||||
// 如果当前节点有子节点,则递归处理每个子节点
|
// 如果当前节点有子节点,则递归处理每个子节点
|
||||||
if (node.Children != null && node.Children.Count > 0)
|
if (node.Children?.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (var child in node.Children)
|
foreach (var child in node.Children)
|
||||||
{
|
{
|
||||||
|
@@ -277,7 +277,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
if (isSuperAdmin)
|
if (isSuperAdmin)
|
||||||
throw Oops.Bah(Localizer["CanotGrantAdmin"]);
|
throw Oops.Bah(Localizer["CanotGrantAdmin"]);
|
||||||
var menuIds = input.GrantInfoList.Select(it => it.MenuId).ToList();//菜单ID
|
var menuIds = input.GrantInfoList.Select(it => it.MenuId).ToList();//菜单ID
|
||||||
var extJsons = input.GrantInfoList.Select(it => it.ToJsonNetString()).ToList();//拓展信息
|
var extJsons = input.GrantInfoList.Select(it => it.ToSystemTextJsonString()).ToList();//拓展信息
|
||||||
var relationRoles = new List<SysRelation>();//要添加的角色资源和授权关系表
|
var relationRoles = new List<SysRelation>();//要添加的角色资源和授权关系表
|
||||||
var sysRole = (await GetAllAsync().ConfigureAwait(false)).FirstOrDefault(it => it.Id == input.Id);//获取角色
|
var sysRole = (await GetAllAsync().ConfigureAwait(false)).FirstOrDefault(it => it.Id == input.Id);//获取角色
|
||||||
|
|
||||||
@@ -338,7 +338,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
ExtJson = new RelationPermission
|
ExtJson = new RelationPermission
|
||||||
{
|
{
|
||||||
ApiUrl = it.ApiRoute,
|
ApiUrl = it.ApiRoute,
|
||||||
}.ToJsonNetString()
|
}.ToSystemTextJsonString()
|
||||||
});
|
});
|
||||||
relationRoles.AddRange(relationRolePer);//合并列表
|
relationRoles.AddRange(relationRolePer);//合并列表
|
||||||
}
|
}
|
||||||
@@ -410,7 +410,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
if (sysRole != null)
|
if (sysRole != null)
|
||||||
{
|
{
|
||||||
await _relationService.SaveRelationBatchAsync(RelationCategoryEnum.RoleHasOpenApiPermission, input.Id,
|
await _relationService.SaveRelationBatchAsync(RelationCategoryEnum.RoleHasOpenApiPermission, input.Id,
|
||||||
input.GrantInfoList.Select(a => (a.ApiUrl, a.ToJsonNetString()))
|
input.GrantInfoList.Select(a => (a.ApiUrl, a.ToSystemTextJsonString()))
|
||||||
, true).ConfigureAwait(false);//添加到数据库
|
, true).ConfigureAwait(false);//添加到数据库
|
||||||
await ClearTokenUtil.DeleteUserCacheByRoleIds(new List<long> { input.Id }).ConfigureAwait(false);//清除角色下用户缓存
|
await ClearTokenUtil.DeleteUserCacheByRoleIds(new List<long> { input.Id }).ConfigureAwait(false);//清除角色下用户缓存
|
||||||
}
|
}
|
||||||
|
@@ -435,7 +435,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
if (sysUser != null)
|
if (sysUser != null)
|
||||||
{
|
{
|
||||||
await _relationService.SaveRelationBatchAsync(RelationCategoryEnum.UserHasOpenApiPermission, input.Id,
|
await _relationService.SaveRelationBatchAsync(RelationCategoryEnum.UserHasOpenApiPermission, input.Id,
|
||||||
input.GrantInfoList.Select(a => (a.ApiUrl, a.ToJsonNetString())),
|
input.GrantInfoList.Select(a => (a.ApiUrl, a.ToSystemTextJsonString())),
|
||||||
true).ConfigureAwait(false);//添加到数据库
|
true).ConfigureAwait(false);//添加到数据库
|
||||||
DeleteUserFromCache(input.Id);
|
DeleteUserFromCache(input.Id);
|
||||||
}
|
}
|
||||||
@@ -466,7 +466,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
var exist = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户信息
|
var exist = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户信息
|
||||||
if (exist != null)
|
if (exist != null)
|
||||||
{
|
{
|
||||||
var isSuperAdmin = exist.Account == RoleConst.SuperAdmin;//判断是否有超管
|
var isSuperAdmin = exist.Id == RoleConst.SuperAdminId;//判断是否有超管
|
||||||
if (isSuperAdmin && !UserManager.SuperAdmin)
|
if (isSuperAdmin && !UserManager.SuperAdmin)
|
||||||
throw Oops.Bah(Localizer["CanotEditAdminUser"]);
|
throw Oops.Bah(Localizer["CanotEditAdminUser"]);
|
||||||
|
|
||||||
@@ -540,7 +540,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||||||
if (sysUser != null)
|
if (sysUser != null)
|
||||||
{
|
{
|
||||||
var isSuperAdmin = (sysUser.Account == RoleConst.SuperAdmin || input.GrantInfoList.Any(a => a == RoleConst.SuperAdminRoleId)) && !UserManager.SuperAdmin;//判断是否有超管
|
var isSuperAdmin = (sysUser.Id == RoleConst.SuperAdminId || input.GrantInfoList.Any(a => a == RoleConst.SuperAdminRoleId)) && !UserManager.SuperAdmin;//判断是否有超管
|
||||||
if (isSuperAdmin)
|
if (isSuperAdmin)
|
||||||
throw Oops.Bah(Localizer["CanotGrantAdmin"]);
|
throw Oops.Bah(Localizer["CanotGrantAdmin"]);
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
public async Task GrantResourceAsync(GrantResourceData input)
|
public async Task GrantResourceAsync(GrantResourceData input)
|
||||||
{
|
{
|
||||||
var menuIds = input.GrantInfoList.Select(it => it.MenuId).ToList();//菜单ID
|
var menuIds = input.GrantInfoList.Select(it => it.MenuId).ToList();//菜单ID
|
||||||
var extJsons = input.GrantInfoList.Select(it => it.ToJsonNetString()).ToList();//拓展信息
|
var extJsons = input.GrantInfoList.Select(it => it.ToSystemTextJsonString()).ToList();//拓展信息
|
||||||
var relationUsers = new List<SysRelation>();//要添加的用户资源和授权关系表
|
var relationUsers = new List<SysRelation>();//要添加的用户资源和授权关系表
|
||||||
var sysUser = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户
|
var sysUser = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户
|
||||||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||||||
@@ -613,7 +613,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
TargetId = it.ApiRoute,
|
TargetId = it.ApiRoute,
|
||||||
Category = RelationCategoryEnum.UserHasPermission,
|
Category = RelationCategoryEnum.UserHasPermission,
|
||||||
ExtJson = new RelationPermission { ApiUrl = it.ApiRoute }
|
ExtJson = new RelationPermission { ApiUrl = it.ApiRoute }
|
||||||
.ToJsonNetString()
|
.ToSystemTextJsonString()
|
||||||
});
|
});
|
||||||
relationUsers.AddRange(relationUserPer);//合并列表
|
relationUsers.AddRange(relationUserPer);//合并列表
|
||||||
}
|
}
|
||||||
@@ -660,7 +660,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
public async Task<bool> DeleteUserAsync(IEnumerable<long> ids)
|
public async Task<bool> DeleteUserAsync(IEnumerable<long> ids)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
var containsSuperAdmin = await db.Queryable<SysUser>().Where(it => it.Account == RoleConst.SuperAdmin && ids.Contains(it.Id)).AnyAsync().ConfigureAwait(false);//判断是否有超管
|
var containsSuperAdmin = await db.Queryable<SysUser>().Where(it => it.Id == RoleConst.SuperAdminId && ids.Contains(it.Id)).AnyAsync().ConfigureAwait(false);//判断是否有超管
|
||||||
if (containsSuperAdmin)
|
if (containsSuperAdmin)
|
||||||
throw Oops.Bah(Localizer["CanotDeleteAdminUser"]);
|
throw Oops.Bah(Localizer["CanotDeleteAdminUser"]);
|
||||||
if (ids.Contains(UserManager.UserId))
|
if (ids.Contains(UserManager.UserId))
|
||||||
@@ -899,7 +899,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
var tenantId = await _sysOrgService.GetTenantIdByOrgIdAsync(sysUser.OrgId, sysOrgList).ConfigureAwait(false);
|
var tenantId = await _sysOrgService.GetTenantIdByOrgIdAsync(sysUser.OrgId, sysOrgList).ConfigureAwait(false);
|
||||||
sysUser.TenantId = tenantId;
|
sysUser.TenantId = tenantId;
|
||||||
|
|
||||||
if (sysUser.Account == RoleConst.SuperAdmin)
|
if (sysUser.Id == RoleConst.SuperAdminId)
|
||||||
{
|
{
|
||||||
var modules = (await _sysResourceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.Category == ResourceCategoryEnum.Module).OrderBy(a => a.SortCode);
|
var modules = (await _sysResourceService.GetAllAsync().ConfigureAwait(false)).Where(a => a.Category == ResourceCategoryEnum.Module).OrderBy(a => a.SortCode);
|
||||||
sysUser.ModuleList = modules.ToList();//模块列表赋值给用户
|
sysUser.ModuleList = modules.ToList();//模块列表赋值给用户
|
||||||
|
@@ -203,7 +203,7 @@ internal sealed class UserCenterService : BaseService<SysUser>, IUserCenterServi
|
|||||||
public async Task UpdateWorkbenchInfoAsync(WorkbenchInfo input)
|
public async Task UpdateWorkbenchInfoAsync(WorkbenchInfo input)
|
||||||
{
|
{
|
||||||
//关系表保存个人工作台
|
//关系表保存个人工作台
|
||||||
await _relationService.SaveRelationAsync(RelationCategoryEnum.UserWorkbenchData, input.Id, null, input.Shortcuts.ToJsonNetString(),
|
await _relationService.SaveRelationAsync(RelationCategoryEnum.UserWorkbenchData, input.Id, null, input.Shortcuts.ToSystemTextJsonString(),
|
||||||
true).ConfigureAwait(false);
|
true).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,9 +10,6 @@
|
|||||||
|
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
|
|
||||||
using ThingsGateway.List;
|
|
||||||
using ThingsGateway.NewLife.Json.Extension;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -169,7 +166,7 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
|
|||||||
public void RemoveAllClientId()
|
public void RemoveAllClientId()
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
db.Updateable<VerificatInfo>().SetColumns("ClientIds", new ConcurrentList<long>().ToJsonNetString()).Where(a => a.Id >= 0).ExecuteCommand();
|
db.Updateable<VerificatInfo>().SetColumns(a => a.ClientIds == null).Where(a => a.Id > 0).ExecuteCommand();
|
||||||
VerificatInfoService.RemoveCache();
|
VerificatInfoService.RemoveCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,8 +13,6 @@ using BootstrapBlazor.Components;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
using SqlSugar;
|
|
||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using ThingsGateway.UnifyResult;
|
using ThingsGateway.UnifyResult;
|
||||||
@@ -28,18 +26,12 @@ public class Startup : AppStartup
|
|||||||
{
|
{
|
||||||
Directory.CreateDirectory("DB");
|
Directory.CreateDirectory("DB");
|
||||||
|
|
||||||
services.AddConfigurableOptions<SqlSugarOptions>();
|
|
||||||
services.AddConfigurableOptions<AdminLogOptions>();
|
services.AddConfigurableOptions<AdminLogOptions>();
|
||||||
services.AddConfigurableOptions<TenantOptions>();
|
services.AddConfigurableOptions<TenantOptions>();
|
||||||
|
|
||||||
services.AddSingleton(typeof(IDataService<>), typeof(BaseService<>));
|
services.AddSingleton<IUserAgentService, UserAgentService>();
|
||||||
services.AddSingleton<ISugarAopService, SugarAopService>();
|
|
||||||
services.AddSingleton<ISugarConfigAopService, SugarConfigAopService>();
|
|
||||||
|
|
||||||
services.AddSingleton<IAppService, AppService>();
|
services.AddSingleton<IAppService, AppService>();
|
||||||
|
|
||||||
StaticConfig.EnableAllWhereIF = true;
|
|
||||||
|
|
||||||
services.AddConfigurableOptions<EmailOptions>();
|
services.AddConfigurableOptions<EmailOptions>();
|
||||||
services.AddConfigurableOptions<HardwareInfoOptions>();
|
services.AddConfigurableOptions<HardwareInfoOptions>();
|
||||||
|
|
||||||
@@ -56,7 +48,6 @@ public class Startup : AppStartup
|
|||||||
|
|
||||||
services.AddSingleton<IVerificatInfoService, VerificatInfoService>();
|
services.AddSingleton<IVerificatInfoService, VerificatInfoService>();
|
||||||
services.AddSingleton<IUserCenterService, UserCenterService>();
|
services.AddSingleton<IUserCenterService, UserCenterService>();
|
||||||
services.AddSingleton<ISugarAopService, SugarAopService>();
|
|
||||||
services.AddSingleton<ISysDictService, SysDictService>();
|
services.AddSingleton<ISysDictService, SysDictService>();
|
||||||
services.AddSingleton<ISysOperateLogService, SysOperateLogService>();
|
services.AddSingleton<ISysOperateLogService, SysOperateLogService>();
|
||||||
services.AddSingleton<IRelationService, RelationService>();
|
services.AddSingleton<IRelationService, RelationService>();
|
||||||
@@ -89,7 +80,7 @@ public class Startup : AppStartup
|
|||||||
DbContext.DbConfigs?.ForEach(it =>
|
DbContext.DbConfigs?.ForEach(it =>
|
||||||
{
|
{
|
||||||
var connection = DbContext.Db.GetConnection(it.ConfigId);//获取数据库连接对象
|
var connection = DbContext.Db.GetConnection(it.ConfigId);//获取数据库连接对象
|
||||||
if (it.InitTable == true)
|
if (it.InitDatabase == true)
|
||||||
connection.DbMaintenance.CreateDatabase();//创建数据库,如果存在则不创建
|
connection.DbMaintenance.CreateDatabase();//创建数据库,如果存在则不创建
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -97,6 +88,21 @@ public class Startup : AppStartup
|
|||||||
CodeFirstUtils.CodeFirst(fullName!);//CodeFirst
|
CodeFirstUtils.CodeFirst(fullName!);//CodeFirst
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var db = DbContext.GetDB<SysOperateLog>();
|
||||||
|
if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite)
|
||||||
|
{
|
||||||
|
if (!db.DbMaintenance.IsAnyIndex("idx_operatelog_optime_date"))
|
||||||
|
{
|
||||||
|
var indexsql = "CREATE INDEX idx_operatelog_optime_date ON sys_operatelog(strftime('%Y-%m-%d', OpTime));";
|
||||||
|
db.Ado.ExecuteCommand(indexsql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
|
||||||
//删除在线用户统计
|
//删除在线用户统计
|
||||||
var verificatInfoService = App.RootServices.GetService<IVerificatInfoService>();
|
var verificatInfoService = App.RootServices.GetService<IVerificatInfoService>();
|
||||||
verificatInfoService.RemoveAllClientId();
|
verificatInfoService.RemoveAllClientId();
|
||||||
|
@@ -18,10 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.4" />
|
|
||||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
|
||||||
<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
|
<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
|
||||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.189" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
||||||
@@ -30,9 +27,9 @@
|
|||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
|
||||||
<PackageReference Include="System.Formats.Asn1" Version="9.0.4" />
|
<PackageReference Include="System.Formats.Asn1" Version="9.0.5" />
|
||||||
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.4" />
|
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="SeedData\Admin\*.json" />
|
<Content Remove="SeedData\Admin\*.json" />
|
||||||
@@ -50,6 +47,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
|
<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
|
||||||
|
<ProjectReference Include="..\ThingsGateway.DB\ThingsGateway.DB.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
namespace ThingsGateway.Admin.Application
|
||||||
|
{
|
||||||
|
/// <summary>Default interface for UserAgentService</summary>
|
||||||
|
public interface IUserAgentService
|
||||||
|
{
|
||||||
|
/// <summary>Gets or sets the settings.</summary>
|
||||||
|
public UserAgentSettings Settings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Parses the specified user agent string.</summary>
|
||||||
|
/// <param name="userAgentString">The user agent string.</param>
|
||||||
|
/// <returns>An UserAgent object</returns>
|
||||||
|
UserAgent? Parse(string userAgentString);
|
||||||
|
}
|
||||||
|
}
|
145
src/Admin/ThingsGateway.Admin.Application/UserAgent/UserAgent.cs
Normal file
145
src/Admin/ThingsGateway.Admin.Application/UserAgent/UserAgent.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parsed UserAgent object
|
||||||
|
/// </summary>
|
||||||
|
public class UserAgent
|
||||||
|
{
|
||||||
|
private readonly UserAgentSettings settings;
|
||||||
|
|
||||||
|
internal string Agent = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this UserAgent is a browser.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if this UserAgent is a browser; otherwise, <c>false</c>.
|
||||||
|
/// </value>
|
||||||
|
public bool IsBrowser { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this UserAgent is a robot.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if this UserAgent is a robot; otherwise, <c>false</c>.
|
||||||
|
/// </value>
|
||||||
|
public bool IsRobot { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this UserAgent is a mobile device.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if this UserAgent is a mobile device; otherwise, <c>false</c>.
|
||||||
|
/// </value>
|
||||||
|
public bool IsMobile { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the platform.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The platform or operating system.
|
||||||
|
/// </value>
|
||||||
|
public string Platform { get; set; } = "";
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the browser.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The browser.
|
||||||
|
/// </value>
|
||||||
|
public string Browser { get; set; } = "";
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the browser version.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The browser version.
|
||||||
|
/// </value>
|
||||||
|
public string BrowserVersion { get; set; } = "";
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the mobile device.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The mobile device.
|
||||||
|
/// </value>
|
||||||
|
public string Mobile { get; set; } = "";
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the robot.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The robot.
|
||||||
|
/// </value>
|
||||||
|
public string Robot { get; set; } = "";
|
||||||
|
|
||||||
|
internal UserAgent(UserAgentSettings settings, string? userAgentString = null)
|
||||||
|
{
|
||||||
|
this.settings = settings;
|
||||||
|
|
||||||
|
if (userAgentString != null)
|
||||||
|
{
|
||||||
|
Agent = userAgentString.Trim();
|
||||||
|
SetPlatform();
|
||||||
|
if (SetRobot()) return;
|
||||||
|
if (SetBrowser()) return;
|
||||||
|
if (SetMobile()) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool SetPlatform()
|
||||||
|
{
|
||||||
|
foreach (var item in settings.Platforms)
|
||||||
|
{
|
||||||
|
if (Regex.IsMatch(Agent, $"{Regex.Escape(item.Key)}", RegexOptions.IgnoreCase))
|
||||||
|
{
|
||||||
|
Platform = item.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Platform = "Unknown Platform";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool SetBrowser()
|
||||||
|
{
|
||||||
|
foreach (var item in settings.Browsers)
|
||||||
|
{
|
||||||
|
var match = Regex.Match(Agent, $@"{item.Key}.*?([0-9\.]+)", RegexOptions.IgnoreCase);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
IsBrowser = true;
|
||||||
|
BrowserVersion = match.Groups[1].Value;
|
||||||
|
Browser = item.Value;
|
||||||
|
SetMobile();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool SetRobot()
|
||||||
|
{
|
||||||
|
foreach (var item in settings.Robots)
|
||||||
|
{
|
||||||
|
if (Regex.IsMatch(Agent, $"{Regex.Escape(item.Key)}", RegexOptions.IgnoreCase))
|
||||||
|
{
|
||||||
|
IsRobot = true;
|
||||||
|
Robot = item.Value;
|
||||||
|
SetMobile();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool SetMobile()
|
||||||
|
{
|
||||||
|
foreach (var item in settings.Mobiles)
|
||||||
|
{
|
||||||
|
if (Agent?.IndexOf(item.Key, StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
|
{
|
||||||
|
IsMobile = true;
|
||||||
|
Mobile = item.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
using ThingsGateway.NewLife.Caching;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The UserAgent service
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="ThingsGateway.Admin.Application.IUserAgentService" />
|
||||||
|
public class UserAgentService : IUserAgentService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the settings.
|
||||||
|
/// </summary>
|
||||||
|
public UserAgentSettings Settings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="UserAgentService"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public UserAgentService()
|
||||||
|
{
|
||||||
|
Settings = new UserAgentSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MemoryCache MemoryCache { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the specified user agent string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userAgentString">The user agent string.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// An UserAgent object
|
||||||
|
/// </returns>
|
||||||
|
public UserAgent? Parse(string? userAgentString)
|
||||||
|
{
|
||||||
|
userAgentString = ((userAgentString?.Length > Settings.UaStringSizeLimit) ? userAgentString?.Trim().Substring(0, Settings.UaStringSizeLimit) : userAgentString?.Trim()) ?? "";
|
||||||
|
return MemoryCache.GetOrAdd(userAgentString, entry =>
|
||||||
|
{
|
||||||
|
return new UserAgent(Settings, userAgentString);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,214 @@
|
|||||||
|
namespace ThingsGateway.Admin.Application
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UserAgent settings container.
|
||||||
|
/// </summary>
|
||||||
|
public class UserAgentSettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum size of the useragent string. Limiting the length of the useragent string protects from hackers sending in extremely long user agent strings.
|
||||||
|
/// </summary>
|
||||||
|
public int UaStringSizeLimit { get; set; } = 512;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a dictionary containing mappings for platforms.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> Platforms { get; } = new()
|
||||||
|
{
|
||||||
|
{"windows nt 10.0", "Windows 10"},
|
||||||
|
{"windows nt 6.3", "Windows 8.1"},
|
||||||
|
{"windows nt 6.2", "Windows 8"},
|
||||||
|
{"windows nt 6.1", "Windows 7"},
|
||||||
|
{"windows nt 6.0", "Windows Vista"},
|
||||||
|
{"windows nt 5.2", "Windows 2003"},
|
||||||
|
{"windows nt 5.1", "Windows XP"},
|
||||||
|
{"windows nt 5.0", "Windows 2000"},
|
||||||
|
{"windows nt 4.0", "Windows NT 4.0"},
|
||||||
|
{"winnt4.0", "Windows NT 4.0"},
|
||||||
|
{"winnt 4.0", "Windows NT"},
|
||||||
|
{"winnt", "Windows NT"},
|
||||||
|
{"windows 98", "Windows 98"},
|
||||||
|
{"win98", "Windows 98"},
|
||||||
|
{"windows 95", "Windows 95"},
|
||||||
|
{"win95", "Windows 95"},
|
||||||
|
{"windows phone", "Windows Phone"},
|
||||||
|
{"windows", "Unknown Windows OS"},
|
||||||
|
{"android", "Android"},
|
||||||
|
{"blackberry", "BlackBerry"},
|
||||||
|
{"iphone", "iOS"},
|
||||||
|
{"ipad", "iOS"},
|
||||||
|
{"ipod", "iOS"},
|
||||||
|
{"os x", "Mac OS X"},
|
||||||
|
{"ppc mac", "Power PC Mac"},
|
||||||
|
{"freebsd", "FreeBSD"},
|
||||||
|
{"ppc", "Macintosh"},
|
||||||
|
{"linux", "Linux"},
|
||||||
|
{"debian", "Debian"},
|
||||||
|
{"sunos", "Sun Solaris"},
|
||||||
|
{"beos", "BeOS"},
|
||||||
|
{"apachebench", "ApacheBench"},
|
||||||
|
{"aix", "AIX"},
|
||||||
|
{"irix", "Irix"},
|
||||||
|
{"osf", "DEC OSF"},
|
||||||
|
{"hp-ux", "HP-UX"},
|
||||||
|
{"netbsd", "NetBSD"},
|
||||||
|
{"bsdi", "BSDi"},
|
||||||
|
{"openbsd", "OpenBSD"},
|
||||||
|
{"gnu", "GNU/Linux"},
|
||||||
|
{"unix", "Unknown Unix OS"},
|
||||||
|
{"symbian", "Symbian OS"},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a dictionary containing mappings for browsers.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> Browsers { get; } = new()
|
||||||
|
{
|
||||||
|
{"Microsoft Outlook", "Microsoft Outlook"},
|
||||||
|
{"OPR", "Opera"},
|
||||||
|
{"Flock", "Flock"},
|
||||||
|
{"Edge", "Edge"},
|
||||||
|
{"Edg", "Edge"},
|
||||||
|
{"Chrome", "Chrome"},
|
||||||
|
{"Opera.*?Version", "Opera"},
|
||||||
|
{"Opera", "Opera"},
|
||||||
|
{"MSIE", "Internet Explorer"},
|
||||||
|
{"Internet Explorer", "Internet Explorer"},
|
||||||
|
{"Trident.* rv" , "Internet Explorer"},
|
||||||
|
{"Shiira", "Shiira"},
|
||||||
|
{"Firefox", "Firefox"},
|
||||||
|
{"Chimera", "Chimera"},
|
||||||
|
{"Phoenix", "Phoenix"},
|
||||||
|
{"Firebird", "Firebird"},
|
||||||
|
{"Camino", "Camino"},
|
||||||
|
{"Netscape", "Netscape"},
|
||||||
|
{"OmniWeb", "OmniWeb"},
|
||||||
|
{"Safari", "Safari"},
|
||||||
|
{"Mozilla", "Mozilla"},
|
||||||
|
{"Konqueror", "Konqueror"},
|
||||||
|
{"icab", "iCab"},
|
||||||
|
{"Lynx", "Lynx"},
|
||||||
|
{"Links", "Links"},
|
||||||
|
{"hotjava", "HotJava"},
|
||||||
|
{"amaya", "Amaya"},
|
||||||
|
{"IBrowse", "IBrowse"},
|
||||||
|
{"Maxthon", "Maxthon"},
|
||||||
|
{"Ubuntu", "Ubuntu Web Browser"},
|
||||||
|
{"Vivaldi", "Vivaldi"},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a dictionary containing mappings for mobiles.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> Mobiles { get; } = new()
|
||||||
|
{
|
||||||
|
// Legacy
|
||||||
|
{"mobileexplorer", "Mobile Explorer"},
|
||||||
|
{"palmsource", "Palm"},
|
||||||
|
{"palmscape", "Palmscape"},
|
||||||
|
// Phones and Manufacturers
|
||||||
|
{"motorola", "Motorola"},
|
||||||
|
{"nokia", "Nokia"},
|
||||||
|
{"palm", "Palm"},
|
||||||
|
{"iphone", "Apple iPhone"},
|
||||||
|
{"ipad", "iPad"},
|
||||||
|
{"ipod", "Apple iPod Touch"},
|
||||||
|
{"sony", "Sony Ericsson"},
|
||||||
|
{"ericsson", "Sony Ericsson"},
|
||||||
|
{"blackberry", "BlackBerry"},
|
||||||
|
{"cocoon", "O2 Cocoon"},
|
||||||
|
{"blazer", "Treo"},
|
||||||
|
{"lg", "LG"},
|
||||||
|
{"amoi", "Amoi"},
|
||||||
|
{"xda", "XDA"},
|
||||||
|
{"mda", "MDA"},
|
||||||
|
{"vario", "Vario"},
|
||||||
|
{"htc", "HTC"},
|
||||||
|
{"samsung", "Samsung"},
|
||||||
|
{"sharp", "Sharp"},
|
||||||
|
{"sie-", "Siemens"},
|
||||||
|
{"alcatel", "Alcatel"},
|
||||||
|
{"benq", "BenQ"},
|
||||||
|
{"ipaq", "HP iPaq"},
|
||||||
|
{"mot-", "Motorola"},
|
||||||
|
{"playstation portable", "PlayStation Portable"},
|
||||||
|
{"playstation 3", "PlayStation 3"},
|
||||||
|
{"playstation vita", "PlayStation Vita"},
|
||||||
|
{"hiptop", "Danger Hiptop"},
|
||||||
|
{"nec-", "NEC"},
|
||||||
|
{"panasonic", "Panasonic"},
|
||||||
|
{"philips", "Philips"},
|
||||||
|
{"sagem", "Sagem"},
|
||||||
|
{"sanyo", "Sanyo"},
|
||||||
|
{"spv", "SPV"},
|
||||||
|
{"zte", "ZTE"},
|
||||||
|
{"sendo", "Sendo"},
|
||||||
|
{"nintendo dsi", "Nintendo DSi"},
|
||||||
|
{"nintendo ds", "Nintendo DS"},
|
||||||
|
{"nintendo 3ds", "Nintendo 3DS"},
|
||||||
|
{"wii", "Nintendo Wii"},
|
||||||
|
{"open web", "Open Web"},
|
||||||
|
{"openweb", "OpenWeb"},
|
||||||
|
// Operating Systems
|
||||||
|
{"android", "Android"},
|
||||||
|
{"symbian", "Symbian"},
|
||||||
|
{"SymbianOS", "SymbianOS"},
|
||||||
|
{"elaine", "Palm"},
|
||||||
|
{"series60", "Symbian S60"},
|
||||||
|
{"windows ce", "Windows CE"},
|
||||||
|
// Browsers
|
||||||
|
{"obigo", "Obigo"},
|
||||||
|
{"netfront", "Netfront Browser"},
|
||||||
|
{"openwave", "Openwave Browser"},
|
||||||
|
{"mobilexplorer", "Mobile Explorer"},
|
||||||
|
{"operamini", "Opera Mini"},
|
||||||
|
{"opera mini", "Opera Mini"},
|
||||||
|
{"opera mobi", "Opera Mobile"},
|
||||||
|
{"fennec", "Firefox Mobile"},
|
||||||
|
// Other
|
||||||
|
{"digital paths", "Digital Paths"},
|
||||||
|
{"avantgo", "AvantGo"},
|
||||||
|
{"xiino", "Xiino"},
|
||||||
|
{"novarra", "Novarra Transcoder"},
|
||||||
|
{"vodafone", "Vodafone"},
|
||||||
|
{"docomo", "NTT DoCoMo"},
|
||||||
|
{"o2", "O2"},
|
||||||
|
// Fallback
|
||||||
|
{"mobile", "Generic Mobile"},
|
||||||
|
{"wireless", "Generic Mobile"},
|
||||||
|
{"j2me", "Generic Mobile"},
|
||||||
|
{"midp", "Generic Mobile"},
|
||||||
|
{"cldc", "Generic Mobile"},
|
||||||
|
{"up.link", "Generic Mobile"},
|
||||||
|
{"up.browser", "Generic Mobile"},
|
||||||
|
{"smartphone", "Generic Mobile"},
|
||||||
|
{"cellphone", "Generic Mobile"},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a dictionary containing mappings for robots.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> Robots { get; } = new()
|
||||||
|
{
|
||||||
|
{"googlebot", "Googlebot"},
|
||||||
|
{"msnbot", "MSNBot"},
|
||||||
|
{"baiduspider", "Baiduspider"},
|
||||||
|
{"bingbot", "Bing"},
|
||||||
|
{"slurp", "Inktomi Slurp"},
|
||||||
|
{"yahoo", "Yahoo"},
|
||||||
|
{"ask jeeves", "Ask Jeeves"},
|
||||||
|
{"fastcrawler", "FastCrawler"},
|
||||||
|
{"infoseek", "InfoSeek Robot 1.0"},
|
||||||
|
{"lycos", "Lycos"},
|
||||||
|
{"yandex", "YandexBot"},
|
||||||
|
{"mediapartners-google", "MediaPartners Google"},
|
||||||
|
{"CRAZYWEBCRAWLER", "Crazy Webcrawler"},
|
||||||
|
{"adsbot-google", "AdsBot Google"},
|
||||||
|
{"feedfetcher-google", "Feedfetcher Google"},
|
||||||
|
{"curious george", "Curious George"},
|
||||||
|
{"ia_archiver", "Alexa Crawler"},
|
||||||
|
{"MJ12bot", "Majestic-12"},
|
||||||
|
{"Uptimebot", "Uptimebot"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class ClearTokenUtil
|
public static class ClearTokenUtil
|
||||||
{
|
{
|
||||||
private static IRelationService RelationService;
|
private static IRelationService RelationService;
|
||||||
private static ISysUserService SysUserService;
|
private static ISysUserService SysUserService;
|
||||||
|
@@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class NoticeUtil
|
public static class NoticeUtil
|
||||||
{
|
{
|
||||||
private static INoticeService NoticeService;
|
private static INoticeService NoticeService;
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class OpenApiUtil
|
public static class OpenApiUtil
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建树节点,传入的列表已经是树结构
|
/// 构建树节点,传入的列表已经是树结构
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class OrgUtil
|
public static class OrgUtil
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造选择项,ID/TITLE
|
/// 构造选择项,ID/TITLE
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class PositionUtil
|
public static class PositionUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class RoleUtil
|
public static class RoleUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class UserUtil
|
public static class UserUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -14,7 +14,7 @@ using ThingsGateway.Extension.Generic;
|
|||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
public class VerificatInfoUtil
|
public static class VerificatInfoUtil
|
||||||
{
|
{
|
||||||
private static IVerificatInfoService VerificatInfoService { get; set; }
|
private static IVerificatInfoService VerificatInfoService { get; set; }
|
||||||
static VerificatInfoUtil()
|
static VerificatInfoUtil()
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
namespace ThingsGateway.Admin.Razor;
|
namespace ThingsGateway.Admin.Razor;
|
||||||
|
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class AdminOperConst
|
public static class AdminOperConst
|
||||||
{
|
{
|
||||||
public const string Add = "新增";
|
public const string Add = "新增";
|
||||||
public const string Delete = "删除";
|
public const string Delete = "删除";
|
||||||
|
@@ -40,6 +40,8 @@ public class BlazorAppContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public SysUser CurrentUser { get; private set; }
|
public SysUser CurrentUser { get; private set; }
|
||||||
|
|
||||||
|
public string? Avatar => UserManager.AvatarUrl.IsNullOrEmpty() ? CurrentUser.Avatar : UserManager.AvatarUrl;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户个人菜单
|
/// 用户个人菜单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -97,7 +99,7 @@ public class BlazorAppContext
|
|||||||
AllResource = sysResources;
|
AllResource = sysResources;
|
||||||
var ids = CurrentUser.ModuleList.Select(a => a.Id).ToHashSet();
|
var ids = CurrentUser.ModuleList.Select(a => a.Id).ToHashSet();
|
||||||
CurrentUser.ModuleList = AllResource.Where(a => ids.Contains(a.Id)).OrderBy(a => a.SortCode).ToList();
|
CurrentUser.ModuleList = AllResource.Where(a => ids.Contains(a.Id)).OrderBy(a => a.SortCode).ToList();
|
||||||
AllMenus = sysResources.Where(a => a.Category == ResourceCategoryEnum.Menu);
|
AllMenus = AllResource.Where(a => a.Category == ResourceCategoryEnum.Menu);
|
||||||
|
|
||||||
if (moduleId == null)
|
if (moduleId == null)
|
||||||
{
|
{
|
||||||
|
@@ -41,7 +41,7 @@ public partial class SessionPage
|
|||||||
{
|
{
|
||||||
var op = new DialogOption()
|
var op = new DialogOption()
|
||||||
{
|
{
|
||||||
IsScrolling = false,
|
IsScrolling = true,
|
||||||
Title = Localizer[nameof(VerificatInfo)],
|
Title = Localizer[nameof(VerificatInfo)],
|
||||||
ShowMaximizeButton = true,
|
ShowMaximizeButton = true,
|
||||||
Class = "dialog-table",
|
Class = "dialog-table",
|
||||||
|
@@ -48,7 +48,7 @@ public partial class SysUserAvatarEdit : IDisposable
|
|||||||
|
|
||||||
private async Task OnAvatarUpload(UploadFile file)
|
private async Task OnAvatarUpload(UploadFile file)
|
||||||
{
|
{
|
||||||
if (file != null && file.File != null)
|
if (file?.File != null)
|
||||||
{
|
{
|
||||||
var format = file.File.ContentType;
|
var format = file.File.ContentType;
|
||||||
ReadAvatarToken ??= new CancellationTokenSource();
|
ReadAvatarToken ??= new CancellationTokenSource();
|
||||||
|
@@ -38,7 +38,7 @@ public partial class UserCenterPage
|
|||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
SysUser = AppContext.CurrentUser.Adapt<SysUser>();
|
SysUser = AppContext.CurrentUser.Adapt<SysUser>();
|
||||||
SysUser.Avatar = AppContext.CurrentUser.Avatar;
|
SysUser.Avatar = AppContext.Avatar;
|
||||||
WorkbenchInfo = (await UserCenterService.GetLoginWorkbenchAsync(SysUser.Id)).Adapt<WorkbenchInfo>();
|
WorkbenchInfo = (await UserCenterService.GetLoginWorkbenchAsync(SysUser.Id)).Adapt<WorkbenchInfo>();
|
||||||
|
|
||||||
await base.OnParametersSetAsync();
|
await base.OnParametersSetAsync();
|
||||||
|
@@ -48,7 +48,7 @@ public partial class UserInfoEditComponent
|
|||||||
|
|
||||||
private async Task OnAvatarUpload(UploadFile file)
|
private async Task OnAvatarUpload(UploadFile file)
|
||||||
{
|
{
|
||||||
if (file != null && file.File != null)
|
if (file?.File != null)
|
||||||
{
|
{
|
||||||
var format = file.File.ContentType;
|
var format = file.File.ContentType;
|
||||||
ReadAvatarToken ??= new CancellationTokenSource();
|
ReadAvatarToken ??= new CancellationTokenSource();
|
||||||
|
@@ -9,10 +9,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.14" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.16" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
|
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@@ -16,7 +16,7 @@ namespace ThingsGateway.Admin.Razor;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class ResourceUtil
|
public static class ResourceUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
// nuget动态加载的程序集
|
// nuget动态加载的程序集
|
||||||
"SupportPackageNamePrefixs": [
|
"SupportPackageNamePrefixs": [
|
||||||
|
"ThingsGateway.SqlSugar",
|
||||||
"ThingsGateway.Admin.Application",
|
"ThingsGateway.Admin.Application",
|
||||||
"ThingsGateway.Admin.Razor",
|
"ThingsGateway.Admin.Razor",
|
||||||
"ThingsGateway.Razor"
|
"ThingsGateway.Razor"
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
// nuget动态加载的程序集
|
// nuget动态加载的程序集
|
||||||
"SupportPackageNamePrefixs": [
|
"SupportPackageNamePrefixs": [
|
||||||
|
"ThingsGateway.SqlSugar",
|
||||||
"ThingsGateway.Admin.Application",
|
"ThingsGateway.Admin.Application",
|
||||||
"ThingsGateway.Admin.Razor",
|
"ThingsGateway.Admin.Razor",
|
||||||
"ThingsGateway.Razor"
|
"ThingsGateway.Razor"
|
||||||
|
1
src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs
Normal file
1
src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
global using ThingsGateway.Admin.Application;
|
@@ -39,19 +39,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-2 mx-1 form-inline">
|
|
||||||
<div class="col-12 col-md-12">
|
|
||||||
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
|
|
||||||
<HeaderTemplate>
|
|
||||||
@Localizer["HardwareInfoChart"]
|
|
||||||
</HeaderTemplate>
|
|
||||||
|
|
||||||
<BodyTemplate>
|
|
||||||
<Chart @ref=CPULineChart OnInitAsync="OnCPUInit" Height="var(--line-chart-height)" Width="100%" OnAfterInitAsync="()=>{chartInit=true;return Task.CompletedTask;}" />
|
|
||||||
</BodyTemplate>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -18,9 +18,6 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
using ThingsGateway.Admin.Application;
|
|
||||||
using ThingsGateway.Admin.Razor;
|
using ThingsGateway.Admin.Razor;
|
||||||
using ThingsGateway.Extension;
|
using ThingsGateway.Extension;
|
||||||
|
|
||||||
@@ -31,118 +28,8 @@ namespace ThingsGateway.AdminServer;
|
|||||||
[IgnoreRolePermission]
|
[IgnoreRolePermission]
|
||||||
[Route("/")]
|
[Route("/")]
|
||||||
[TabItemOption(Text = "Home", Icon = "fas fa-house")]
|
[TabItemOption(Text = "Home", Icon = "fas fa-house")]
|
||||||
public partial class AdminIndex : IDisposable
|
public partial class AdminIndex
|
||||||
{
|
{
|
||||||
[Inject]
|
|
||||||
private IHardwareJob HardwareJob { get; set; }
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
_ = RunTimerAsync();
|
|
||||||
base.OnInitialized();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Disposed { get; set; }
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RunTimerAsync()
|
|
||||||
{
|
|
||||||
while (!Disposed)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (chartInit)
|
|
||||||
await CPULineChart.Update(ChartAction.Update);
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
await Task.Delay(30000);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
NewLife.Log.XTrace.WriteException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 曲线
|
|
||||||
|
|
||||||
private bool chartInit { get; set; }
|
|
||||||
private Chart CPULineChart { get; set; }
|
|
||||||
private ChartDataSource? ChartDataSource { get; set; }
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
[NotNull]
|
|
||||||
private IStringLocalizer<HistoryHardwareInfo> HistoryHardwareInfoLocalizer { get; set; }
|
|
||||||
|
|
||||||
private async Task<ChartDataSource> OnCPUInit()
|
|
||||||
{
|
|
||||||
if (ChartDataSource == null)
|
|
||||||
{
|
|
||||||
var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos();
|
|
||||||
ChartDataSource = new ChartDataSource();
|
|
||||||
ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)];
|
|
||||||
ChartDataSource.Options.X.Title = Localizer["DateTime"];
|
|
||||||
ChartDataSource.Options.Y.Title = Localizer["Data"];
|
|
||||||
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz"));
|
|
||||||
ChartDataSource.Data.Add(new ChartDataset()
|
|
||||||
{
|
|
||||||
Tension = 0.4f,
|
|
||||||
PointRadius = 1,
|
|
||||||
Label = HistoryHardwareInfoLocalizer[nameof(HistoryHardwareInfo.CpuUsage)],
|
|
||||||
Data = hisHardwareInfos.Select(a => (object)a.CpuUsage),
|
|
||||||
});
|
|
||||||
ChartDataSource.Data.Add(new ChartDataset()
|
|
||||||
{
|
|
||||||
Tension = 0.4f,
|
|
||||||
PointRadius = 1,
|
|
||||||
Label = HistoryHardwareInfoLocalizer[nameof(HistoryHardwareInfo.MemoryUsage)],
|
|
||||||
Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage),
|
|
||||||
});
|
|
||||||
|
|
||||||
ChartDataSource.Data.Add(new ChartDataset()
|
|
||||||
{
|
|
||||||
Tension = 0.4f,
|
|
||||||
PointRadius = 1,
|
|
||||||
Label = HistoryHardwareInfoLocalizer[nameof(HistoryHardwareInfo.DriveUsage)],
|
|
||||||
Data = hisHardwareInfos.Select(a => (object)a.DriveUsage),
|
|
||||||
});
|
|
||||||
|
|
||||||
ChartDataSource.Data.Add(new ChartDataset()
|
|
||||||
{
|
|
||||||
ShowPointStyle = false,
|
|
||||||
Tension = 0.4f,
|
|
||||||
PointRadius = 1,
|
|
||||||
Label = HistoryHardwareInfoLocalizer[nameof(HistoryHardwareInfo.Temperature)],
|
|
||||||
Data = hisHardwareInfos.Select(a => (object)a.Temperature),
|
|
||||||
});
|
|
||||||
|
|
||||||
ChartDataSource.Data.Add(new ChartDataset()
|
|
||||||
{
|
|
||||||
Tension = 0.4f,
|
|
||||||
PointRadius = 1,
|
|
||||||
Label = HistoryHardwareInfoLocalizer[nameof(HistoryHardwareInfo.Battery)],
|
|
||||||
Data = hisHardwareInfos.Select(a => (object)a.Battery),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos();
|
|
||||||
ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz"));
|
|
||||||
ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage);
|
|
||||||
ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage);
|
|
||||||
ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage);
|
|
||||||
ChartDataSource.Data[3].Data = hisHardwareInfos.Select(a => (object)a.Temperature);
|
|
||||||
ChartDataSource.Data[4].Data = hisHardwareInfos.Select(a => (object)a.Battery);
|
|
||||||
}
|
|
||||||
return ChartDataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion 曲线
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private BlazorAppContext AppContext { get; set; }
|
private BlazorAppContext AppContext { get; set; }
|
||||||
|
@@ -13,8 +13,6 @@ using Microsoft.Extensions.Localization;
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using ThingsGateway.Admin.Application;
|
|
||||||
|
|
||||||
namespace ThingsGateway.AdminServer;
|
namespace ThingsGateway.AdminServer;
|
||||||
|
|
||||||
public partial class AccessDenied
|
public partial class AccessDenied
|
||||||
|
@@ -20,11 +20,11 @@ using Microsoft.Extensions.Options;
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using ThingsGateway.Admin.Application;
|
|
||||||
using ThingsGateway.DataEncryption;
|
using ThingsGateway.DataEncryption;
|
||||||
using ThingsGateway.NewLife.Extension;
|
using ThingsGateway.NewLife.Extension;
|
||||||
using ThingsGateway.Razor;
|
using ThingsGateway.Razor;
|
||||||
|
|
||||||
|
|
||||||
namespace ThingsGateway.AdminServer;
|
namespace ThingsGateway.AdminServer;
|
||||||
|
|
||||||
public partial class Login
|
public partial class Login
|
||||||
|
@@ -48,7 +48,7 @@
|
|||||||
<CultureChooser />
|
<CultureChooser />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Logout ImageUrl="@(AppContext.CurrentUser.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
|
<Logout ImageUrl="@(AppContext.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
|
||||||
<LinkTemplate>
|
<LinkTemplate>
|
||||||
<a href=@("/") class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["系统首页"]</a>
|
<a href=@("/") class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["系统首页"]</a>
|
||||||
|
|
||||||
|
@@ -17,7 +17,6 @@ using Microsoft.Extensions.Options;
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using ThingsGateway.Admin.Application;
|
|
||||||
using ThingsGateway.Admin.Razor;
|
using ThingsGateway.Admin.Razor;
|
||||||
using ThingsGateway.Razor;
|
using ThingsGateway.Razor;
|
||||||
|
|
||||||
@@ -27,38 +26,6 @@ public partial class MainLayout : IDisposable
|
|||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
IStringLocalizer<ThingsGateway.Razor._Imports> RazorLocalizer { get; set; }
|
IStringLocalizer<ThingsGateway.Razor._Imports> RazorLocalizer { get; set; }
|
||||||
private Task OnRefresh(ContextMenuItem item, object? context)
|
|
||||||
{
|
|
||||||
if (context is TabItem tabItem)
|
|
||||||
{
|
|
||||||
_tab.Refresh(tabItem);
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnClose(ContextMenuItem item, object? context)
|
|
||||||
{
|
|
||||||
if (context is TabItem tabItem)
|
|
||||||
{
|
|
||||||
await _tab.RemoveTab(tabItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task OnCloseOther(ContextMenuItem item, object? context)
|
|
||||||
{
|
|
||||||
if (context is TabItem tabItem)
|
|
||||||
{
|
|
||||||
_tab.ActiveTab(tabItem);
|
|
||||||
}
|
|
||||||
_tab.CloseOtherTabs();
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task OnCloseAll(ContextMenuItem item, object? context)
|
|
||||||
{
|
|
||||||
_tab.CloseAllTabs();
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 全局通知
|
#region 全局通知
|
||||||
|
|
||||||
|
@@ -40,7 +40,8 @@ public class SingleFilePublish : ISingleFilePublish
|
|||||||
"ThingsGateway.NewLife.X",
|
"ThingsGateway.NewLife.X",
|
||||||
"ThingsGateway.Razor",
|
"ThingsGateway.Razor",
|
||||||
"ThingsGateway.Admin.Razor" ,
|
"ThingsGateway.Admin.Razor" ,
|
||||||
"ThingsGateway.Admin.Application"
|
"ThingsGateway.Admin.Application",
|
||||||
|
"ThingsGateway.SqlSugar",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,22 +10,23 @@
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
|
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||||
|
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
|
|
||||||
using ThingsGateway.Admin.Application;
|
|
||||||
using ThingsGateway.Admin.Razor;
|
using ThingsGateway.Admin.Razor;
|
||||||
using ThingsGateway.Extension;
|
using ThingsGateway.Extension;
|
||||||
using ThingsGateway.Logging;
|
|
||||||
using ThingsGateway.NewLife.Caching;
|
using ThingsGateway.NewLife.Caching;
|
||||||
|
|
||||||
namespace ThingsGateway.AdminServer;
|
namespace ThingsGateway.AdminServer;
|
||||||
@@ -85,6 +86,7 @@ public class Startup : AppStartup
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
services.AddMvcFilter<RequestAuditFilter>();
|
||||||
services.AddControllers()
|
services.AddControllers()
|
||||||
.AddNewtonsoftJson(options => SetNewtonsoftJsonSetting(options.SerializerSettings))
|
.AddNewtonsoftJson(options => SetNewtonsoftJsonSetting(options.SerializerSettings))
|
||||||
//.AddXmlSerializerFormatters()
|
//.AddXmlSerializerFormatters()
|
||||||
@@ -157,7 +159,9 @@ public class Startup : AppStartup
|
|||||||
{
|
{
|
||||||
options.WriteFilter = (logMsg) =>
|
options.WriteFilter = (logMsg) =>
|
||||||
{
|
{
|
||||||
return true;
|
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested && logMsg.LogLevel >= LogLevel.Warning) return false;
|
||||||
|
if (string.IsNullOrEmpty(logMsg.Message)) return false;
|
||||||
|
else return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
options.MessageFormat = (logMsg) =>
|
options.MessageFormat = (logMsg) =>
|
||||||
@@ -207,39 +211,39 @@ public class Startup : AppStartup
|
|||||||
#region api日志
|
#region api日志
|
||||||
|
|
||||||
//Monitor日志配置
|
//Monitor日志配置
|
||||||
services.AddMonitorLogging(options =>
|
//services.AddMonitorLogging(options =>
|
||||||
{
|
//{
|
||||||
options.JsonIndented = true;// 是否美化 JSON
|
// options.JsonIndented = true;// 是否美化 JSON
|
||||||
options.GlobalEnabled = false;//全局启用
|
// options.GlobalEnabled = false;//全局启用
|
||||||
options.ConfigureLogger((logger, logContext, context) =>
|
// options.ConfigureLogger((logger, logContext, context) =>
|
||||||
{
|
// {
|
||||||
var httpContext = context.HttpContext;//获取httpContext
|
// var httpContext = context.HttpContext;//获取httpContext
|
||||||
|
|
||||||
//获取客户端信息
|
// //获取客户端信息
|
||||||
var client = App.GetService<IAppService>().ClientInfo;
|
// var client = App.GetService<IAppService>().UserAgent;
|
||||||
// 获取控制器/操作描述器
|
// // 获取控制器/操作描述器
|
||||||
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
|
// var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
|
||||||
//操作名称默认是控制器名加方法名,自定义操作名称要在action上加Description特性
|
// //操作名称默认是控制器名加方法名,自定义操作名称要在action上加Description特性
|
||||||
var option = $"{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}";
|
// var option = $"{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}";
|
||||||
|
|
||||||
var desc = App.CreateLocalizerByType(controllerActionDescriptor.ControllerTypeInfo.AsType())[controllerActionDescriptor.MethodInfo.Name];
|
// var desc = App.CreateLocalizerByType(controllerActionDescriptor.ControllerTypeInfo.AsType())[controllerActionDescriptor.MethodInfo.Name];
|
||||||
//获取特性
|
// //获取特性
|
||||||
option = desc.Value;//则将操作名称赋值为控制器上写的title
|
// option = desc.Value;//则将操作名称赋值为控制器上写的title
|
||||||
|
|
||||||
logContext.Set(LoggingConst.CateGory, option);//传操作名称
|
// logContext.Set(LoggingConst.CateGory, option);//传操作名称
|
||||||
logContext.Set(LoggingConst.Operation, option);//传操作名称
|
// logContext.Set(LoggingConst.Operation, option);//传操作名称
|
||||||
logContext.Set(LoggingConst.Client, client);//客户端信息
|
// logContext.Set(LoggingConst.Client, client);//客户端信息
|
||||||
logContext.Set(LoggingConst.Path, httpContext.Request.Path.Value);//请求地址
|
// logContext.Set(LoggingConst.Path, httpContext.Request.Path.Value);//请求地址
|
||||||
logContext.Set(LoggingConst.Method, httpContext.Request.Method);//请求方法
|
// logContext.Set(LoggingConst.Method, httpContext.Request.Method);//请求方法
|
||||||
});
|
// });
|
||||||
});
|
//});
|
||||||
|
|
||||||
//日志写入数据库配置
|
//日志写入数据库配置
|
||||||
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
|
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
|
||||||
{
|
{
|
||||||
options.WriteFilter = (logMsg) =>
|
options.WriteFilter = (logMsg) =>
|
||||||
{
|
{
|
||||||
return logMsg.LogName == "System.Logging.LoggingMonitor";//只写入LoggingMonitor日志
|
return logMsg.LogName == "System.Logging.RequestAudit";
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -291,6 +295,21 @@ public class Startup : AppStartup
|
|||||||
services.AddAuthorizationCore();
|
services.AddAuthorizationCore();
|
||||||
services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
|
services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
|
||||||
services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
|
services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
|
||||||
|
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||||
|
#else
|
||||||
|
var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||||
|
#endif
|
||||||
|
services.AddDataProtection()
|
||||||
|
.PersistKeysToFileSystem(new DirectoryInfo("keys"))
|
||||||
|
.ProtectKeysWithCertificate(certificate)
|
||||||
|
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
|
||||||
|
{
|
||||||
|
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
|
||||||
|
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -349,12 +368,6 @@ public class Startup : AppStartup
|
|||||||
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
|
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.Use(async (context, next) =>
|
|
||||||
{
|
|
||||||
context.Response.Headers.Append("ThingsGateway", "ThingsGateway");
|
|
||||||
await next().ConfigureAwait(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// 特定文件类型(文件后缀)处理
|
// 特定文件类型(文件后缀)处理
|
||||||
var contentTypeProvider = GetFileExtensionContentTypeProvider();
|
var contentTypeProvider = GetFileExtensionContentTypeProvider();
|
||||||
|
@@ -45,7 +45,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.4" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!--安装服务守护-->
|
<!--安装服务守护-->
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||||
@@ -54,8 +54,8 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -72,6 +72,9 @@
|
|||||||
<None Update="pm2-linux.json">
|
<None Update="pm2-linux.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="ThingsGateway.pfx">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
<None Update="thingsgateway.service">
|
<None Update="thingsgateway.service">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
BIN
src/Admin/ThingsGateway.AdminServer/ThingsGateway.pfx
Normal file
BIN
src/Admin/ThingsGateway.AdminServer/ThingsGateway.pfx
Normal file
Binary file not shown.
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// 授权用户常量
|
/// 授权用户常量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class ClaimConst
|
public static class ClaimConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 账号
|
/// 账号
|
||||||
@@ -31,6 +31,11 @@ public class ClaimConst
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string UserId = "UserId";
|
public const string UserId = "UserId";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AvatarUrl
|
||||||
|
/// </summary>
|
||||||
|
public const string AvatarUrl = "AvatarUrl";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证Id
|
/// 验证Id
|
||||||
/// </summary>
|
/// </summary>
|
@@ -225,7 +225,7 @@ public static class SqlSugarExtensions
|
|||||||
|
|
||||||
private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
|
private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
|
||||||
{
|
{
|
||||||
if (basePageInput != null && basePageInput.SortField != null)
|
if (basePageInput?.SortField != null)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < basePageInput.SortField.Count; i++)
|
for (int i = 0; i < basePageInput.SortField.Count; i++)
|
||||||
{
|
{
|
11
src/Admin/ThingsGateway.DB/GlobalUsings.cs
Normal file
11
src/Admin/ThingsGateway.DB/GlobalUsings.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
global using ThingsGateway.NewLife.Extension;
|
@@ -0,0 +1,20 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public class ClaimsPrincipalService : IClaimsPrincipalService
|
||||||
|
{
|
||||||
|
|
||||||
|
public ClaimsPrincipal? User => App.User;
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public interface IClaimsPrincipalService
|
||||||
|
{
|
||||||
|
public ClaimsPrincipal? User { get; }
|
||||||
|
}
|
@@ -17,10 +17,10 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
|
|
||||||
public class SugarAopService : ISugarAopService
|
public class SugarAopService : ISugarAopService
|
||||||
{
|
{
|
||||||
private IAppService _appService;
|
private IClaimsPrincipalService _claimsPrincipalService;
|
||||||
public SugarAopService(IAppService appService)
|
public SugarAopService(IClaimsPrincipalService appService)
|
||||||
{
|
{
|
||||||
_appService = appService;
|
_claimsPrincipalService = appService;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Aop设置
|
/// Aop设置
|
||||||
@@ -85,7 +85,7 @@ public class SugarAopService : ISugarAopService
|
|||||||
if (entityInfo.PropertyName == nameof(BaseEntity.CreateTime))
|
if (entityInfo.PropertyName == nameof(BaseEntity.CreateTime))
|
||||||
entityInfo.SetValue(DateTime.Now);
|
entityInfo.SetValue(DateTime.Now);
|
||||||
|
|
||||||
if (_appService.User != null)
|
if (_claimsPrincipalService.User != null)
|
||||||
{
|
{
|
||||||
//创建人
|
//创建人
|
||||||
if (entityInfo.PropertyName == nameof(BaseEntity.CreateUserId))
|
if (entityInfo.PropertyName == nameof(BaseEntity.CreateUserId))
|
||||||
@@ -103,7 +103,7 @@ public class SugarAopService : ISugarAopService
|
|||||||
if (entityInfo.PropertyName == nameof(BaseEntity.UpdateTime))
|
if (entityInfo.PropertyName == nameof(BaseEntity.UpdateTime))
|
||||||
entityInfo.SetValue(DateTime.Now);
|
entityInfo.SetValue(DateTime.Now);
|
||||||
//更新人
|
//更新人
|
||||||
if (_appService.User != null)
|
if (_claimsPrincipalService.User != null)
|
||||||
{
|
{
|
||||||
if (entityInfo.PropertyName == nameof(BaseEntity.UpdateUserId))
|
if (entityInfo.PropertyName == nameof(BaseEntity.UpdateUserId))
|
||||||
entityInfo.SetValue(UserManager.UserId);
|
entityInfo.SetValue(UserManager.UserId);
|
||||||
@@ -117,6 +117,25 @@ public class SugarAopService : ISugarAopService
|
|||||||
db.Aop.DataExecuted = (value, entity) =>
|
db.Aop.DataExecuted = (value, entity) =>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
db.Aop.OnLogExecuted = (sql, pars) =>
|
||||||
|
{
|
||||||
|
//执行时间超过1秒
|
||||||
|
if (db.Ado.SqlExecutionTime.TotalSeconds > 1)
|
||||||
|
{
|
||||||
|
//代码CS文件名
|
||||||
|
var fileName = db.Ado.SqlStackTrace.FirstFileName;
|
||||||
|
//代码行数
|
||||||
|
var fileLine = db.Ado.SqlStackTrace.FirstLine;
|
||||||
|
//方法名
|
||||||
|
var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;
|
||||||
|
|
||||||
|
DbContext.WriteLog($"{fileName}-{FirstMethodName}-{fileLine} 执行时间超过1秒");
|
||||||
|
DbContext.WriteLogWithSql(UtilMethods.GetNativeSql(sql, pars));
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -80,7 +80,9 @@ public static class DbContext
|
|||||||
{
|
{
|
||||||
db.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings
|
db.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings
|
||||||
{
|
{
|
||||||
SqlServerCodeFirstNvarchar = true//设置默认nvarchar
|
SqlServerCodeFirstNvarchar = true, //设置默认nvarchar
|
||||||
|
|
||||||
|
IsNoReadXmlDescription = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@@ -25,7 +25,7 @@ namespace ThingsGateway.Admin.Application;
|
|||||||
/// 种子数据工具类
|
/// 种子数据工具类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public class SeedDataUtil
|
public static class SeedDataUtil
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取List列表
|
/// 获取List列表
|
@@ -24,6 +24,11 @@ public sealed class SqlSugarOption : ConnectionConfig
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool InitSeedData { get; set; } = false;
|
public bool InitSeedData { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化数据库
|
||||||
|
/// </summary>
|
||||||
|
public bool InitDatabase { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化表
|
/// 初始化表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -32,7 +37,7 @@ public sealed class SqlSugarOption : ConnectionConfig
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否控制台显示Sql语句
|
/// 是否控制台显示Sql语句
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsShowSql { get; set; }
|
public bool? IsShowSql { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新数据
|
/// 更新数据
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user