Compare commits
95 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
57a4038577 | ||
![]() |
b78a76e60f | ||
![]() |
28bd751d44 | ||
![]() |
dcba7b9810 | ||
![]() |
7f1a741ce6 | ||
![]() |
ca2b17d433 | ||
![]() |
af589eac10 | ||
![]() |
573670f1f5 | ||
![]() |
f3ec85a03d | ||
![]() |
c94308454f | ||
![]() |
c9c2b2b69d | ||
![]() |
40574b776f | ||
![]() |
f426c1533d | ||
![]() |
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 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -365,4 +365,5 @@ FodyWeavers.xsd
|
||||
/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_event = false:silent
|
||||
|
||||
|
||||
dotnet_diagnostic.CA2208.severity = none
|
||||
dotnet_diagnostic.CA2008.severity = none
|
||||
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_diagnostic.RCS1146.severity = warning
|
||||
dotnet_diagnostic.RCS1059.severity = none
|
||||
dotnet_diagnostic.RCS1138.severity = suggestion
|
||||
|
||||
dotnet_code_quality.CA1822.api_surface = private, internal
|
@@ -64,24 +64,31 @@ public sealed class OperDescAttribute : MoAttribute
|
||||
|
||||
public override void OnException(MethodContext context)
|
||||
{
|
||||
//插入异常日志
|
||||
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
|
||||
{
|
||||
//插入异常日志
|
||||
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||
|
||||
log.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
||||
log.ExeStatus = false;//操作状态为失败
|
||||
if (context.Exception is AppFriendlyException exception)
|
||||
log.ExeMessage = exception?.Message;
|
||||
else
|
||||
log.ExeMessage = context.Exception?.ToString();
|
||||
log.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
||||
log.ExeStatus = false;//操作状态为失败
|
||||
if (context.Exception is AppFriendlyException exception)
|
||||
log.ExeMessage = exception?.Message;
|
||||
else
|
||||
log.ExeMessage = context.Exception?.ToString();
|
||||
|
||||
OperDescAttribute.WriteToQueue(log);
|
||||
OperDescAttribute.WriteToQueue(log);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSuccess(MethodContext context)
|
||||
{
|
||||
//插入操作日志
|
||||
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||
OperDescAttribute.WriteToQueue(log);
|
||||
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
|
||||
{
|
||||
|
||||
//插入操作日志
|
||||
SysOperateLog log = GetOperLog(LocalizerType, context);
|
||||
OperDescAttribute.WriteToQueue(log);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +122,7 @@ public sealed class OperDescAttribute : MoAttribute
|
||||
private SysOperateLog GetOperLog(Type? localizerType, MethodContext context)
|
||||
{
|
||||
var methodBase = context.Method;
|
||||
var clientInfo = AppService.ClientInfo;
|
||||
var userAgent = AppService.UserAgent;
|
||||
string? paramJson = null;
|
||||
if (IsRecordPar)
|
||||
{
|
||||
@@ -127,10 +134,10 @@ public sealed class OperDescAttribute : MoAttribute
|
||||
{
|
||||
parametersDict[parametersInfo[i].Name!] = args[i];
|
||||
}
|
||||
paramJson = parametersDict.ToJsonNetString();
|
||||
paramJson = parametersDict.ToSystemTextJsonString();
|
||||
}
|
||||
var result = context.ReturnValue;
|
||||
var resultJson = IsRecordPar ? result?.ToJsonNetString() : null;
|
||||
var resultJson = IsRecordPar ? result?.ToSystemTextJsonString() : null;
|
||||
//操作日志表实体
|
||||
var log = new SysOperateLog
|
||||
{
|
||||
@@ -138,8 +145,8 @@ public sealed class OperDescAttribute : MoAttribute
|
||||
Category = LogCateGoryEnum.Operate,
|
||||
ExeStatus = true,
|
||||
OpIp = AppService?.RemoteIpAddress ?? string.Empty,
|
||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
||||
OpBrowser = userAgent?.Browser,
|
||||
OpOs = userAgent?.Platform,
|
||||
OpTime = DateTime.Now,
|
||||
OpAccount = UserManager.UserAccount,
|
||||
ReqUrl = null,
|
||||
|
@@ -13,14 +13,16 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// <summary>
|
||||
/// 需要角色授权权限
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public sealed class RolePermissionAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 忽略角色授权权限
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
|
||||
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;
|
||||
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class CacheConst
|
||||
public static class CacheConst
|
||||
{
|
||||
/// <summary>
|
||||
/// Token表缓存Key
|
||||
|
@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// <summary>
|
||||
/// 通讯器常量
|
||||
/// </summary>
|
||||
public class HubConst
|
||||
public static class HubConst
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统HubUrl
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// 资源表常量
|
||||
/// </summary>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class ResourceConst
|
||||
public static class ResourceConst
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统内置编码
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// 角色常量
|
||||
/// </summary>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class RoleConst
|
||||
public static class RoleConst
|
||||
{
|
||||
/// <summary>
|
||||
/// api角色
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// SqlSugar系统常量
|
||||
/// </summary>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class SqlSugarConst
|
||||
public static class SqlSugarConst
|
||||
{
|
||||
/// <summary>
|
||||
/// DB_Admin
|
||||
|
@@ -8,6 +8,7 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
[ApiDescriptionSettings(false)]
|
||||
[Route("api/auth")]
|
||||
[LoggingMonitor]
|
||||
[RequestAudit]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly IAuthService _authService;
|
||||
@@ -27,14 +28,31 @@ public class AuthController : ControllerBase
|
||||
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
[LoginLog]
|
||||
public Task<LoginOutput> LoginAsync([FromBody] LoginInput 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")]
|
||||
[Authorize]
|
||||
[IgnoreRolePermission]
|
||||
[LogoutLog]
|
||||
public Task LogoutAsync()
|
||||
{
|
||||
return _authService.LoginOutAsync();
|
||||
|
@@ -25,7 +25,8 @@ namespace ThingsGateway.Admin.Application;
|
||||
[Description("登录")]
|
||||
[Route("openapi/auth")]
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
[LoggingMonitor]
|
||||
[RequestAudit]
|
||||
[ApiController]
|
||||
public class OpenApiController : ControllerBase
|
||||
{
|
||||
private readonly IAuthService _authService;
|
||||
|
@@ -15,16 +15,13 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
[Route("api/[controller]/[action]")]
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
public class TestController : ControllerBase
|
||||
{
|
||||
[HttpPost]
|
||||
public Task Test(string data)
|
||||
[HttpGet]
|
||||
public void Test()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
}
|
||||
|
@@ -10,10 +10,10 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
[SugarTable("sys_dict", TableDescription = "字典表")]
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -10,10 +10,10 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
|
@@ -10,10 +10,10 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -14,10 +14,10 @@ using Microsoft.AspNetCore.Components.Routing;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
|
@@ -10,10 +10,10 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
|
@@ -12,11 +12,11 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
|
@@ -10,9 +10,8 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.List;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -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>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string WorkingSet { get; set; }
|
||||
public int WorkingSet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
|
@@ -17,6 +17,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
using ThingsGateway.Extension;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Caching;
|
||||
using ThingsGateway.NewLife.Threading;
|
||||
using ThingsGateway.Schedule;
|
||||
|
||||
@@ -51,11 +52,20 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
|
||||
#endregion 属性
|
||||
|
||||
private MemoryCache MemoryCache = new() { };
|
||||
private const string CacheKey = "HistoryHardwareInfo";
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos()
|
||||
{
|
||||
using var db = DbContext.Db.GetConnectionScopeWithAttr<HistoryHardwareInfo>().CopyNew();
|
||||
return await db.Queryable<HistoryHardwareInfo>().ToListAsync().ConfigureAwait(false);
|
||||
var historyHardwareInfos = MemoryCache.Get<List<HistoryHardwareInfo>>(CacheKey);
|
||||
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;
|
||||
@@ -94,13 +104,13 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
{
|
||||
HardwareInfo.MachineInfo.Refresh();
|
||||
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;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!error)
|
||||
_logger.LogWarning(ex, _localizer["GetHardwareInfoFail"]);
|
||||
_logger.LogWarning(ex, "Get Hardwareinfo Fail");
|
||||
error = true;
|
||||
}
|
||||
|
||||
@@ -116,17 +126,22 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
var his = new HistoryHardwareInfo()
|
||||
{
|
||||
Date = TimerX.Now,
|
||||
DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToString("F2"),
|
||||
Battery = (HardwareInfo.MachineInfo.Battery * 100).ToString("F2"),
|
||||
DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(),
|
||||
Battery = (HardwareInfo.MachineInfo.Battery * 100).ToInt(),
|
||||
MemoryUsage = (HardwareInfo.WorkingSet),
|
||||
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToString("F2"),
|
||||
Temperature = (HardwareInfo.MachineInfo.Temperature).ToString("F2"),
|
||||
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(),
|
||||
Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(),
|
||||
};
|
||||
await db.Insertable(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
|
||||
MemoryCache.Remove(CacheKey);
|
||||
}
|
||||
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;
|
||||
@@ -137,7 +152,7 @@ public class HardwareJob : IJob, IHardwareJob
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!error)
|
||||
_logger.LogWarning(ex, _localizer["GetHardwareInfoFail"]);
|
||||
_logger.LogWarning(ex, "Get Hardwareinfo Fail");
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -19,23 +19,23 @@ public class HistoryHardwareInfo
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "磁盘使用率")]
|
||||
public string DriveUsage { get; set; }
|
||||
public int DriveUsage { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "内存")]
|
||||
public string MemoryUsage { get; set; }
|
||||
public int MemoryUsage { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "CPU使用率")]
|
||||
public string CpuUsage { get; set; }
|
||||
public int CpuUsage { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "温度")]
|
||||
public string Temperature { get; set; }
|
||||
public int Temperature { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "电池")]
|
||||
public string Battery { get; set; }
|
||||
public int Battery { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[SugarColumn(ColumnDescription = "时间")]
|
||||
|
@@ -1,466 +1,402 @@
|
||||
{
|
||||
"ThingsGateway.Admin.Application.AppConfig": {
|
||||
"LoginPolicy": "LoginPolicy",
|
||||
"PagePolicy": "PagePolicy",
|
||||
"PasswordPolicy": "PasswordPolicy",
|
||||
"WebsitePolicy": "WebsitePolicy"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
"AuthController": "Login API",
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
"AuthErrorMax": "Account password error, will be locked for {1} minutes after exceeding {0} times, error count {2}",
|
||||
"MustDesc": "Password needs to be encrypted with DESC before passing",
|
||||
"OrgDisable": "The affiliated company/department has been deactivated, please contact the administrator",
|
||||
"PasswordError": "Too many password errors, please try again in {0} minutes",
|
||||
"SingleLoginWarn": "Your account is logged in elsewhere",
|
||||
"TenantNull": "The tenant does not exist",
|
||||
"UserDisable": "Account {0} has been disabled",
|
||||
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
||||
"UserNull": "User {0} does not exist"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||
"CreateOrgId": "CreateOrgId"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||
"CreateTime": "CreateTime",
|
||||
"CreateUser": "CreateUser",
|
||||
"SortCode": "SortCode",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"UpdateUser": "UpdateUser"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||
"UserExpire": "User expired, please login again"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysUser": {
|
||||
"Disable": "Disable",
|
||||
"Enable": "Enable",
|
||||
"GrantRole": "GrantRole",
|
||||
"ExitVerificat": "You have been forcibly logged out",
|
||||
"PasswordEdited": "Password changed, logged out",
|
||||
|
||||
"Avatar": "Avatar",
|
||||
"Account": "Account",
|
||||
"Account.Required": "Account.Required",
|
||||
"Password": "Password",
|
||||
"Status": "Status",
|
||||
"Phone": "Phone",
|
||||
"Email": "Email",
|
||||
"LastLoginIp": "LastLoginIp",
|
||||
"LastLoginDevice": "LastLoginDevice",
|
||||
"LastLoginTime": "LastLoginTime",
|
||||
"LastLoginAddress": "LastLoginAddress",
|
||||
"LatestLoginIp": "LatestLoginIp",
|
||||
"LatestLoginTime": "LatestLoginTime",
|
||||
"LatestLoginDevice": "LatestLoginDevice",
|
||||
"LatestLoginAddress": "LatestLoginAddress",
|
||||
"SortCode": "SortCode",
|
||||
"CreateTime": "CreateTime",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"OrgNames": "OrgNames",
|
||||
"PositionName": "PositionName",
|
||||
"OrgId": "Org",
|
||||
"PositionId": "Position",
|
||||
"DirectorId": "Director",
|
||||
|
||||
"CheckSelf": "Prohibit {0} yourself",
|
||||
"CanotDeleteAdminUser": "Cannot delete built-in super admin user",
|
||||
"CanotEditAdminUser": "Cannot edit super admin user",
|
||||
"CanotGrantAdmin": "Cannot assign admins roles",
|
||||
"EmailDup": "Duplicate email {0} exists",
|
||||
"AccountDup": "Duplicate account {0} exists",
|
||||
"CanotDeleteSelf": "Cannot delete yourself",
|
||||
"EmailError": "Email format error {0}",
|
||||
"PhoneError": "Phone number format error {0}",
|
||||
"NoOrg": "The organization does not exist",
|
||||
"DirectorSelf": "Cannot set oneself as the supervisor",
|
||||
|
||||
"DemoCanotUpdatePassword": "DEMO environment does not allow password modification",
|
||||
"OldPasswordError": "Incorrect old password",
|
||||
"ConfirmPasswordDiff": "Passwords entered twice are inconsistent",
|
||||
"PasswordLengthLess": "Password length cannot be less than {0}",
|
||||
"PasswordMustNum ": "Password must contain numbers",
|
||||
"PasswordMustLow": "Password must contain lowercase letters",
|
||||
"PasswordMustUpp": "Password must contain uppercase letters",
|
||||
"PasswordMustSpecial": "Password must contain special characters"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysRole": {
|
||||
"Code": "Code",
|
||||
"Name": "Name",
|
||||
"Name.Required": "{0} is required",
|
||||
"Category": "Category",
|
||||
"SortCode": "Sort",
|
||||
"CreateTime": "CreateTime",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"OrgId": "Org",
|
||||
"Global": "Global",
|
||||
"Status": "Status",
|
||||
"CanotDeleteAdmin": "Cannot delete built-in super admin role",
|
||||
"CanotEditAdmin": "Cannot edit super admin role",
|
||||
"CanotGrantAdmin": "Cannot assign admins roles",
|
||||
"NameDup": "Duplicate role name {0}",
|
||||
|
||||
"OrgNotNull": "Organization cannot be null",
|
||||
"SameOrgNameDup": "Duplicate role name exists: {0}",
|
||||
"CannotRoleScopeAll": "Organization role cannot select global data scope",
|
||||
"CodeDup": "Duplicate code exists: {0}"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
|
||||
"Global": "Global",
|
||||
"Org": "Org"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DataScopeEnum": {
|
||||
"SCOPE_SELF": "Self",
|
||||
"SCOPE_ALL": "All",
|
||||
"SCOPE_ORG": "OnlyOrg",
|
||||
"SCOPE_ORG_CHILD": "OrgChild",
|
||||
"SCOPE_ORG_DEFINE": "Define"
|
||||
"SCOPE_ORG_DEFINE": "Define",
|
||||
"SCOPE_SELF": "Self"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DefaultDataScope": {
|
||||
"ScopeCategory": "DataScope",
|
||||
"ScopeDefineOrgIdList": "DefineOrgList"
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.Admin.Application.SysResource": {
|
||||
"Title": "Title",
|
||||
"Module": "Module",
|
||||
"Title.Required": "{0} is required",
|
||||
"Href.Required": "{0} is required",
|
||||
"Icon": "Icon",
|
||||
"Href": "Path",
|
||||
"Code": "Code",
|
||||
"Category": "Category",
|
||||
"Target": "Target",
|
||||
"NavLinkMatch": "NavLinkMatch",
|
||||
"SortCode": "Sort",
|
||||
"CreateTime": "CreateTime",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"ParentId": "Parent",
|
||||
"ResourceDup": "Duplicate name {0} exists",
|
||||
"ResourceParentChoiceSelf": "Parent cannot choose itself",
|
||||
"ResourceParentNull": "Parent does not exist {0}",
|
||||
"NotFoundResource": "System exception, menu not found",
|
||||
"ModuleIdDiff": "Module is inconsistent with parent menu",
|
||||
"CanotDeleteSystemResource": "Cannot delete system resource {0}",
|
||||
"ResourceMenuHrefNotNull": "Menu href cannot null"
|
||||
"ThingsGateway.Admin.Application.DictTypeEnum": {
|
||||
"Define": "Business",
|
||||
"System": "System"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
|
||||
"TargetId": "Target",
|
||||
"ContainsChild": "ContainsChild",
|
||||
"ContainsPosition": "ContainsPosition"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysPosition": {
|
||||
"Category.Required": "{0} is a required field",
|
||||
"Name.Required": "{0} is a required field",
|
||||
"Code.Required": "{0} is a required field",
|
||||
"OrgId.MinValue": "{0} is a required field",
|
||||
"Category": "Category",
|
||||
"Name": "Name",
|
||||
"Code": "Code",
|
||||
"Status": "Status",
|
||||
"OrgId": "Organization",
|
||||
"Remark": "Remarks",
|
||||
"SortCode": "SortCode",
|
||||
"CreateTime": "CreateTime",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"Dup": "Duplicate position exists with Category {0} and Name {1}",
|
||||
"CodeDup": "Duplicate code {0} exists",
|
||||
"NameDup": "Duplicate name {0} exists",
|
||||
"CanotContainsSelf": "Cannot contain itself",
|
||||
"TargetNameDup": "Target node has duplicate name {0}",
|
||||
"ParentChoiceSelf": "Parent cannot be itself",
|
||||
"ParentNull": "Parent does not exist {0}",
|
||||
"DeleteUserFirst": "Please remove the users under the position first"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysOrg": {
|
||||
"Category.Required": "{0} is a required field",
|
||||
"Name.Required": "{0} is a required field",
|
||||
"Code.Required": "{0} is a required field",
|
||||
"Category": "Category",
|
||||
"Name": "Name",
|
||||
"Code": "Code",
|
||||
"Status": "Status",
|
||||
"ParentId": "ParentOrg",
|
||||
"Names": "Names",
|
||||
"Remark": "Remarks",
|
||||
"DirectorId": "Director",
|
||||
"SortCode": "SortCode",
|
||||
"CreateTime": "CreateTime",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"Dup": "Duplicate organization exists with Category {0} and Name {1}",
|
||||
"CodeDup": "Duplicate code {0} exists",
|
||||
"NameDup": "Duplicate name {0} exists",
|
||||
"CanotContainsSelf": "Cannot contain itself",
|
||||
"TargetNameDup": "Target node has duplicate name {0}",
|
||||
"ParentChoiceSelf": "Parent cannot be itself",
|
||||
"ParentNull": "Parent does not exist {0}",
|
||||
"DeleteUserFirst": "Please remove the users under the organization first",
|
||||
"DeleteRoleFirst": "Please remove the roles under the organization first",
|
||||
"DeletePositionFirst": "Please remove the positions under the organization first",
|
||||
"RootOrg": "Unable to create top-level organization"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.OrgEnum": {
|
||||
"COMPANY": "Company",
|
||||
"DEPT": "Dept"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
|
||||
"HIGH": "High",
|
||||
"MIDDLE": "Middle",
|
||||
"LOW": "Low"
|
||||
},
|
||||
//controller
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
//auth
|
||||
"AuthController": "Login API",
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
//auth
|
||||
"TestController": "Test API",
|
||||
"Test": "Test"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
//auth
|
||||
"OpenApiAuthController": "Login API",
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.FileService": {
|
||||
"FileNullError": "File cannot be empty",
|
||||
"FileLengthError": "File size cannot exceed {0} M",
|
||||
"FileNullError": "File cannot be empty",
|
||||
"FileTypeError": "Not supported format {0}"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
||||
"TokenOver": "Login has expired, please login again",
|
||||
"NoPermission": "Access denied, no permission"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
|
||||
"TenantNull": "The tenant does not exist",
|
||||
"OrgDisable": "The affiliated company/department has been deactivated, please contact the administrator",
|
||||
|
||||
"SingleLoginWarn": "Your account is logged in elsewhere",
|
||||
"UserNull": "User {0} does not exist",
|
||||
"MustDesc": "Password needs to be encrypted with DESC before passing",
|
||||
"PasswordError": "Too many password errors, please try again in {0} minutes",
|
||||
"UserDisable": "Account {0} has been disabled",
|
||||
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
||||
"AuthErrorMax": "Account password error, will be locked for {1} minutes after exceeding {0} times, error count {2}"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||
"Environment": "HostEnvironment",
|
||||
"FrameworkDescription": ".NETFramework",
|
||||
"OsArchitecture": "System Architecture",
|
||||
"UUID": "UUID",
|
||||
"UpdateTime": "UpdateTime"
|
||||
"UpdateTime": "UpdateTime",
|
||||
"UUID": "UUID"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
|
||||
"DriveUsage": "Disk Usage",
|
||||
"MemoryUsage": "Memory",
|
||||
"CpuUsage": "CPU Usage",
|
||||
"Temperature": "Temperature",
|
||||
"Battery": "Battery"
|
||||
},
|
||||
|
||||
|
||||
//oper
|
||||
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
||||
//dict
|
||||
"SaveDict": "Modify dictionary",
|
||||
"DeleteDict": "Delete dictionary",
|
||||
"EditLoginPolicy": "Modify login policy",
|
||||
"EditPasswordPolicy": "Modify password policy",
|
||||
"EditPagePolicy": "Modify page policy",
|
||||
"EditWebsitePolicy": "Modify website settings",
|
||||
//operlog
|
||||
"DeleteOperLog": "Delete operation log",
|
||||
"ExportOperLog": "Export operation log",
|
||||
|
||||
//resource
|
||||
"SaveResource": "Modify resource",
|
||||
"DeleteResource": "Delete resource",
|
||||
|
||||
//role
|
||||
"SaveRole": "Modify role",
|
||||
"DeleteRole": "Delete role",
|
||||
"RoleGrantResource": "Role grant resource",
|
||||
"RoleGrantUser": "Role grant user",
|
||||
"RoleGrantApiPermission": "Role grant OpenApi",
|
||||
"GrantApi": "GrantApi",
|
||||
"GrantUser": "GrantUser",
|
||||
"GrantRole": "GrantRole",
|
||||
"GrantResource": "GrantResource",
|
||||
//user
|
||||
"SaveUser": "Modify user",
|
||||
"DeleteuSER": "Delete user",
|
||||
"ResetPassword": "Reset pw",
|
||||
"UserGrantRole": "User grant role",
|
||||
"UserGrantResource": "User grant resource",
|
||||
"UserGrantApiPermission": "User grant OpenApi",
|
||||
|
||||
//usercenter
|
||||
"UpdateUserInfo": "Update personal information",
|
||||
"WorkbenchInfo": "Update personal workbench",
|
||||
"UpdatePassword": "Update personal password",
|
||||
|
||||
//session
|
||||
"ExitVerificat": "Force token off",
|
||||
"ExitSession": "Force session off",
|
||||
|
||||
|
||||
"CopyOrg": "Copy Organization",
|
||||
"DeleteOrg": "Delete Organization",
|
||||
"SaveOrg": "Save Organization",
|
||||
|
||||
"DeletePosition": "Delete Position",
|
||||
"SavePosition": "Save Position",
|
||||
|
||||
"NoPermission": "No Permission",
|
||||
|
||||
|
||||
"CopyResource": "CopyResource",
|
||||
"ChangeParentResource": "ChangeParentResource"
|
||||
},
|
||||
|
||||
|
||||
//service
|
||||
|
||||
"ThingsGateway.Admin.Application.HardwareJob": {
|
||||
"GetHardwareInfoFail": "Get Hardwareinfo Fail"
|
||||
},
|
||||
|
||||
//dto
|
||||
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
||||
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
|
||||
"Battery": "Battery",
|
||||
"CpuUsage": "CPU Usage",
|
||||
"DriveUsage": "Disk Usage",
|
||||
"MemoryUsage": "Memory",
|
||||
"Temperature": "Temperature"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
|
||||
"Exception": "Exception",
|
||||
"Login": "Login",
|
||||
"Logout": "Logout",
|
||||
"Operate": "Operation"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogEnum": {
|
||||
"FAIL": "Fail",
|
||||
"SUCCESS": "Success"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginInput": {
|
||||
"Account": "Account",
|
||||
"OrgId": "Org"
|
||||
"Account.Required": "{0} is required",
|
||||
"Password": "Password",
|
||||
"Password.Required": "{0} is required"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginPolicy": {
|
||||
"ErrorCount": "Login error count lock threshold",
|
||||
"ErrorCount.MinValue": "{0} value is too small",
|
||||
"ErrorLockTime": "Login error lock duration (min)",
|
||||
"ErrorLockTime.MinValue": "{0} value is too small",
|
||||
"ErrorResetTime": "Login error count expiration duration (min)",
|
||||
"ErrorResetTime.MinValue": "{0} value is too small",
|
||||
"SingleOpen": "Single user login switch",
|
||||
"VerificatExpireTime": "Login expiration time (min)",
|
||||
"VerificatExpireTime.MinValue": "{0} value is too small"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogoutInput": {
|
||||
"VerificatId.Required": "{0} is required"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout",
|
||||
"OpenApiAuthController": "Login API"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OperateLogPageInput": {
|
||||
"Account": "Account",
|
||||
"Category": "Category",
|
||||
"SearchDate": "SearchDate"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
||||
"ChangeParentResource": "ChangeParentResource",
|
||||
"CopyOrg": "Copy Organization",
|
||||
"CopyResource": "CopyResource",
|
||||
"DeleteDict": "Delete dictionary",
|
||||
"DeleteOperLog": "Delete operation log",
|
||||
"DeleteOrg": "Delete Organization",
|
||||
"DeletePosition": "Delete Position",
|
||||
"DeleteResource": "Delete resource",
|
||||
"DeleteRole": "Delete role",
|
||||
"DeleteuSER": "Delete user",
|
||||
"EditLoginPolicy": "Modify login policy",
|
||||
"EditPagePolicy": "Modify page policy",
|
||||
"EditPasswordPolicy": "Modify password policy",
|
||||
"EditWebsitePolicy": "Modify website settings",
|
||||
"ExitSession": "Force session off",
|
||||
"ExitVerificat": "Force token off",
|
||||
"ExportOperLog": "Export operation log",
|
||||
"GrantApi": "GrantApi",
|
||||
"GrantResource": "GrantResource",
|
||||
"GrantRole": "GrantRole",
|
||||
"GrantUser": "GrantUser",
|
||||
"NoPermission": "No Permission",
|
||||
"ResetPassword": "Reset pw",
|
||||
"RoleGrantApiPermission": "Role grant OpenApi",
|
||||
"RoleGrantResource": "Role grant resource",
|
||||
"RoleGrantUser": "Role grant user",
|
||||
"SaveDict": "Modify dictionary",
|
||||
"SaveOrg": "Save Organization",
|
||||
"SavePosition": "Save Position",
|
||||
"SaveResource": "Modify resource",
|
||||
"SaveRole": "Modify role",
|
||||
"SaveUser": "Modify user",
|
||||
"UpdatePassword": "Update personal password",
|
||||
"UpdateUserInfo": "Update personal information",
|
||||
"UserGrantApiPermission": "User grant OpenApi",
|
||||
"UserGrantResource": "User grant resource",
|
||||
"UserGrantRole": "User grant role",
|
||||
"WorkbenchInfo": "Update personal workbench"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OrgEnum": {
|
||||
"COMPANY": "Company",
|
||||
"DEPT": "Dept"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PagePolicy": {
|
||||
"Razor": "Default homepage",
|
||||
"Shortcuts": "Default shortcuts"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PasswordPolicy": {
|
||||
"DefaultPassword": "Default user password",
|
||||
"DefaultPassword.Required": "{0} is required",
|
||||
"PasswordContainChar": "Contain special characters",
|
||||
"PasswordContainLower": "Contain lowercase letters",
|
||||
"PasswordContainNum": "Contain numbers",
|
||||
"PasswordContainUpper": "Contain uppercase letters",
|
||||
"PasswordMinLen": "Minimum password length",
|
||||
"PasswordMinLen.MinValue": "{0} value is too small"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
|
||||
"HIGH": "High",
|
||||
"LOW": "Low",
|
||||
"MIDDLE": "Middle"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
|
||||
"Button": "Button",
|
||||
"Menu": "Menu",
|
||||
"Module": "Module"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
|
||||
"Module": "Module",
|
||||
"Href": "Path",
|
||||
"Module": "Module",
|
||||
"Title": "Title"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WorkbenchInfo": {
|
||||
"Razor": "Homepage",
|
||||
"Shortcuts": "Shortcuts"
|
||||
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
|
||||
"Global": "Global",
|
||||
"Org": "Org"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
|
||||
"Password": "Password",
|
||||
"NewPassword": "New password",
|
||||
"ConfirmPassword": "Confirm password",
|
||||
"Password.Required": "{0} is required",
|
||||
"NewPassword.Required": "{0} is required",
|
||||
"ConfirmPassword.Required": "{0} is required"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.VerificatInfo": {
|
||||
"Expire": "Expire(min)",
|
||||
"Online": "Online",
|
||||
"VerificatTimeout": "VerificatTimeout",
|
||||
"Device": "Device",
|
||||
"LoginIp": "LoginIp",
|
||||
"LoginTime": "LoginTime"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SessionOutput": {
|
||||
"Account": "Account",
|
||||
"Online": "Online status",
|
||||
"LatestLoginIp": "Latest login IP",
|
||||
"LatestLoginTime": "Latest login time",
|
||||
"Online": "Online status",
|
||||
"VerificatCount": "Token count"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysDict": {
|
||||
"Category.Required": "{0} is required",
|
||||
"Name.Required": "{0} is required",
|
||||
"Code.Required": "{0} is required",
|
||||
"Category": "Category",
|
||||
"Name": "Name",
|
||||
"Category.Required": "{0} is required",
|
||||
"Code": "Code",
|
||||
"Remark": "Remark",
|
||||
"SortCode": "Sort",
|
||||
"CreateTime": "CreateTime",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"Code.Required": "{0} is required",
|
||||
"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}",
|
||||
"Name": "Name",
|
||||
"Name.Required": "{0} is required",
|
||||
"Remark": "Remark"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysOperateLog": {
|
||||
"Category": "Category",
|
||||
"ClassName": "ClassName",
|
||||
"ExeMessage": "ExeMessage",
|
||||
"MethodName": "MethodName",
|
||||
"ParamJson": "ParamJson",
|
||||
"ReqMethod": "RequestMethod",
|
||||
"ReqUrl": "RequestUrl",
|
||||
"ResultJson": "ResultJson",
|
||||
"Category": "Category",
|
||||
"ExeStatus": "ExeStatus",
|
||||
"MethodName": "MethodName",
|
||||
"Name": "Name",
|
||||
"OpAccount": "OpAccount",
|
||||
"OpBrowser": "OpBrowser",
|
||||
"OpIp": "OpIp",
|
||||
"OpOs": "OpOs",
|
||||
"OpTime": "OpTime",
|
||||
"ParamJson": "ParamJson",
|
||||
"ReqMethod": "RequestMethod",
|
||||
"ReqUrl": "RequestUrl",
|
||||
"ResultJson": "ResultJson",
|
||||
"VerificatId": "VerificatId"
|
||||
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OperateLogPageInput": {
|
||||
"SearchDate": "SearchDate",
|
||||
"Account": "Account",
|
||||
"Category": "Category"
|
||||
"ThingsGateway.Admin.Application.SysOrg": {
|
||||
"CanotContainsSelf": "Cannot contain itself",
|
||||
"Category": "Category",
|
||||
"Category.Required": "{0} is a required field",
|
||||
"Code": "Code",
|
||||
"Code.Required": "{0} is a required field",
|
||||
"CodeDup": "Duplicate code {0} exists",
|
||||
"DeletePositionFirst": "Please remove the positions under the organization first",
|
||||
"DeleteRoleFirst": "Please remove the roles under the organization first",
|
||||
"DeleteUserFirst": "Please remove the users under the organization first",
|
||||
"DirectorId": "Director",
|
||||
"Dup": "Duplicate organization exists with Category {0} and Name {1}",
|
||||
"Name": "Name",
|
||||
"Name.Required": "{0} is a required field",
|
||||
"NameDup": "Duplicate name {0} exists",
|
||||
"Names": "Names",
|
||||
"ParentChoiceSelf": "Parent cannot be itself",
|
||||
"ParentId": "ParentOrg",
|
||||
"ParentNull": "Parent does not exist {0}",
|
||||
"Remark": "Remarks",
|
||||
"RootOrg": "Unable to create top-level organization",
|
||||
"Status": "Status",
|
||||
"TargetNameDup": "Target node has duplicate name {0}"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginInput": {
|
||||
"Account": "Account",
|
||||
"Password": "Password",
|
||||
"Account.Required": "{0} is required",
|
||||
"Password.Required": "{0} is required"
|
||||
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
|
||||
"ContainsChild": "ContainsChild",
|
||||
"ContainsPosition": "ContainsPosition",
|
||||
"TargetId": "Target"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogoutInput": {
|
||||
"VerificatId.Required": "{0} is required"
|
||||
"ThingsGateway.Admin.Application.SysPosition": {
|
||||
"CanotContainsSelf": "Cannot contain itself",
|
||||
"Category": "Category",
|
||||
"Category.Required": "{0} is a required field",
|
||||
"Code": "Code",
|
||||
"Code.Required": "{0} is a required field",
|
||||
"CodeDup": "Duplicate code {0} exists",
|
||||
"DeleteUserFirst": "Please remove the users under the position first",
|
||||
"Dup": "Duplicate position exists with Category {0} and Name {1}",
|
||||
"Name": "Name",
|
||||
"Name.Required": "{0} is a required field",
|
||||
"NameDup": "Duplicate name {0} exists",
|
||||
"OrgId": "Organization",
|
||||
"OrgId.MinValue": "{0} is a required field",
|
||||
"ParentChoiceSelf": "Parent cannot be itself",
|
||||
"ParentNull": "Parent does not exist {0}",
|
||||
"Remark": "Remarks",
|
||||
"Status": "Status",
|
||||
"TargetNameDup": "Target node has duplicate name {0}"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AppConfig": {
|
||||
"LoginPolicy": "LoginPolicy",
|
||||
"PasswordPolicy": "PasswordPolicy",
|
||||
"PagePolicy": "PagePolicy",
|
||||
"WebsitePolicy": "WebsitePolicy"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginPolicy": {
|
||||
"SingleOpen": "Single user login switch",
|
||||
"ErrorLockTime": "Login error lock duration (min)",
|
||||
"ErrorResetTime": "Login error count expiration duration (min)",
|
||||
"ErrorCount": "Login error count lock threshold",
|
||||
"VerificatExpireTime": "Login expiration time (min)",
|
||||
"ErrorLockTime.MinValue": "{0} value is too small",
|
||||
"ErrorResetTime.MinValue": "{0} value is too small",
|
||||
"ErrorCount.MinValue": "{0} value is too small",
|
||||
"VerificatExpireTime.MinValue": "{0} value is too small"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PagePolicy": {
|
||||
"Shortcuts": "Default shortcuts",
|
||||
"Razor": "Default homepage"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PasswordPolicy": {
|
||||
"DefaultPassword": "Default user password",
|
||||
"DefaultPassword.Required": "{0} is required",
|
||||
"PasswordMinLen": "Minimum password length",
|
||||
"PasswordMinLen.MinValue": "{0} value is too small",
|
||||
"PasswordContainNum": "Contain numbers",
|
||||
"PasswordContainLower": "Contain lowercase letters",
|
||||
"PasswordContainUpper": "Contain uppercase letters",
|
||||
"PasswordContainChar": "Contain special characters"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WebsitePolicy": {
|
||||
"WebStatus": "WebStatus",
|
||||
"CloseTip": "CloseTip",
|
||||
"CloseTip.Required": "{0} is required"
|
||||
},
|
||||
//enum
|
||||
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
|
||||
"ThingsGateway.Admin.Application.SysResource": {
|
||||
"CanotDeleteSystemResource": "Cannot delete system resource {0}",
|
||||
"Category": "Category",
|
||||
"Code": "Code",
|
||||
"Href": "Path",
|
||||
"Href.Required": "{0} is required",
|
||||
"Icon": "Icon",
|
||||
"Module": "Module",
|
||||
"Menu": "Menu",
|
||||
"Button": "Button"
|
||||
"ModuleIdDiff": "Module is inconsistent with parent menu",
|
||||
"NavLinkMatch": "NavLinkMatch",
|
||||
"NotFoundResource": "System exception, menu not found",
|
||||
"ParentId": "Parent",
|
||||
"ResourceDup": "Duplicate name {0} exists",
|
||||
"ResourceMenuHrefNotNull": "Menu href cannot null",
|
||||
"ResourceParentChoiceSelf": "Parent cannot choose itself",
|
||||
"ResourceParentNull": "Parent does not exist {0}",
|
||||
"Target": "Target",
|
||||
"Title": "Title",
|
||||
"Title.Required": "{0} is required"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysRole": {
|
||||
"CannotRoleScopeAll": "Organization role cannot select global data scope",
|
||||
"CanotDeleteAdmin": "Cannot delete built-in super admin role",
|
||||
"CanotEditAdmin": "Cannot edit super admin role",
|
||||
"CanotGrantAdmin": "Cannot assign admins roles",
|
||||
"Category": "Category",
|
||||
"Code": "Code",
|
||||
"CodeDup": "Duplicate code exists: {0}",
|
||||
"Global": "Global",
|
||||
"Name": "Name",
|
||||
"Name.Required": "{0} is required",
|
||||
"NameDup": "Duplicate role name {0}",
|
||||
"OrgId": "Org",
|
||||
"OrgNotNull": "Organization cannot be null",
|
||||
"SameOrgNameDup": "Duplicate role name exists: {0}",
|
||||
"Status": "Status"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysUser": {
|
||||
"Account": "Account",
|
||||
"Account.Required": "Account.Required",
|
||||
"AccountDup": "Duplicate account {0} exists",
|
||||
"Avatar": "Avatar",
|
||||
"CanotDeleteAdminUser": "Cannot delete built-in super admin user",
|
||||
"CanotDeleteSelf": "Cannot delete yourself",
|
||||
"CanotEditAdminUser": "Cannot edit super admin user",
|
||||
"CanotGrantAdmin": "Cannot assign admins roles",
|
||||
"CheckSelf": "Prohibit {0} yourself",
|
||||
"ConfirmPasswordDiff": "Passwords entered twice are inconsistent",
|
||||
"DemoCanotUpdatePassword": "DEMO environment does not allow password modification",
|
||||
"DirectorId": "Director",
|
||||
"DirectorSelf": "Cannot set oneself as the supervisor",
|
||||
"Disable": "Disable",
|
||||
"Email": "Email",
|
||||
"EmailDup": "Duplicate email {0} exists",
|
||||
"EmailError": "Email format error {0}",
|
||||
"Enable": "Enable",
|
||||
"ExitVerificat": "You have been forcibly logged out",
|
||||
"GrantRole": "GrantRole",
|
||||
"LastLoginAddress": "LastLoginAddress",
|
||||
"LastLoginDevice": "LastLoginDevice",
|
||||
"LastLoginIp": "LastLoginIp",
|
||||
"LastLoginTime": "LastLoginTime",
|
||||
"LatestLoginAddress": "LatestLoginAddress",
|
||||
"LatestLoginDevice": "LatestLoginDevice",
|
||||
"LatestLoginIp": "LatestLoginIp",
|
||||
"LatestLoginTime": "LatestLoginTime",
|
||||
"NoOrg": "The organization does not exist",
|
||||
"OldPasswordError": "Incorrect old password",
|
||||
"OrgId": "Org",
|
||||
"OrgNames": "OrgNames",
|
||||
"Password": "Password",
|
||||
"PasswordEdited": "Password changed, logged out",
|
||||
"PasswordLengthLess": "Password length cannot be less than {0}",
|
||||
"PasswordMustLow": "Password must contain lowercase letters",
|
||||
"PasswordMustNum": "Password must contain numbers",
|
||||
"PasswordMustSpecial": "Password must contain special characters",
|
||||
"PasswordMustUpp": "Password must contain uppercase letters",
|
||||
"Phone": "Phone",
|
||||
"PhoneError": "Phone number format error {0}",
|
||||
"PositionId": "Position",
|
||||
"PositionName": "PositionName",
|
||||
"Status": "Status"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.TargetEnum": {
|
||||
"_self": "Current window",
|
||||
"_blank": "New window",
|
||||
"_parent": "Parent window",
|
||||
"_self": "Current window",
|
||||
"_top": "Top window"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DictTypeEnum": {
|
||||
"System": "System",
|
||||
"Define": "Business"
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
"Test": "Test",
|
||||
"TestController": "Test API"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
|
||||
"Login": "Login",
|
||||
"Logout": "Logout",
|
||||
"Operate": "Operation",
|
||||
"Exception": "Exception"
|
||||
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
||||
"NoPermission": "Access denied, no permission",
|
||||
"TokenOver": "Login has expired, please login again"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.LogEnum": {
|
||||
"SUCCESS": "Success",
|
||||
"FAIL": "Fail"
|
||||
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
|
||||
"ConfirmPassword": "Confirm password",
|
||||
"ConfirmPassword.Required": "{0} is required",
|
||||
"NewPassword": "New password",
|
||||
"NewPassword.Required": "{0} is required",
|
||||
"Password": "Password",
|
||||
"Password.Required": "{0} is required"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
||||
"Account": "Account",
|
||||
"OrgId": "Org"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.VerificatInfo": {
|
||||
"Device": "Device",
|
||||
"Expire": "Expire(min)",
|
||||
"LoginIp": "LoginIp",
|
||||
"LoginTime": "LoginTime",
|
||||
"Online": "Online",
|
||||
"VerificatTimeout": "VerificatTimeout"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WebsitePolicy": {
|
||||
"CloseTip": "CloseTip",
|
||||
"CloseTip.Required": "{0} is required",
|
||||
"WebStatus": "WebStatus"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WorkbenchInfo": {
|
||||
"Razor": "Homepage",
|
||||
"Shortcuts": "Shortcuts"
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,469 +1,402 @@
|
||||
{
|
||||
"ThingsGateway.Admin.Application.AppConfig": {
|
||||
"LoginPolicy": "登录策略",
|
||||
"PagePolicy": "页面设置",
|
||||
"PasswordPolicy": "密码策略",
|
||||
"WebsitePolicy": "网站设置"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
"AuthController": "登录API",
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
|
||||
"MustDesc": "密码需要DESC加密后传入",
|
||||
"OrgDisable": "所属公司/部门已停用,请联系管理员",
|
||||
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
|
||||
"SingleLoginWarn": "您的账号已在别处登录",
|
||||
"TenantNull": "租户不存在",
|
||||
"UserDisable": "账号 {0} 已停用",
|
||||
"UserNoModule": "该账号未分配模块,请联系管理员",
|
||||
"UserNull": "用户 {0} 不存在"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||
"CreateOrgId": "创建机构Id"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||
"CreateTime": "创建时间",
|
||||
"CreateUser": "创建人",
|
||||
"SortCode": "排序",
|
||||
"UpdateTime": "更新时间",
|
||||
"UpdateUser": "更新人"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||
"UserExpire": "用户登录已过期,请重新登录"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysUser": {
|
||||
"Disable": "禁用",
|
||||
"Enable": "启用",
|
||||
"GrantRole": "分配角色",
|
||||
"ExitVerificat": "您已被强制下线",
|
||||
"PasswordEdited": "密码被修改,已退出登录",
|
||||
"Avatar": "头像",
|
||||
"Account": "账号",
|
||||
"Account.Required": " {0} 是必填项",
|
||||
"Password": "密码",
|
||||
"Status": "状态",
|
||||
"Phone": "手机",
|
||||
"Email": "邮箱",
|
||||
"LastLoginIp": "上次登录ip",
|
||||
"LastLoginDevice": "上次登录设备",
|
||||
"LastLoginTime": "上次登录时间",
|
||||
"LastLoginAddress": "上次登录地点",
|
||||
"LatestLoginIp": "最新登录ip",
|
||||
"LatestLoginTime": "最新登录时间",
|
||||
"LatestLoginDevice": "最新登录设备",
|
||||
"LatestLoginAddress": "最新登录地点",
|
||||
"SortCode": "排序",
|
||||
"CreateTime": "创建时间",
|
||||
"UpdateTime": "更新时间",
|
||||
"OrgNames": "机构名称",
|
||||
"PositionName": "职位名称",
|
||||
"OrgId": "机构",
|
||||
"PositionId": "职位",
|
||||
"DirectorId": "主管",
|
||||
"CheckSelf": "禁止 {0} 自己",
|
||||
"CanotDeleteAdminUser": "不可删除系统内置超管用户",
|
||||
"CanotEditAdminUser": "不可编辑超管用户",
|
||||
"CanotGrantAdmin": "不能分配超管角色",
|
||||
"EmailDup": "存在重复的邮箱 {0}",
|
||||
"AccountDup": "存在重复的账号 {0}",
|
||||
"CanotDeleteSelf": "不可删除自己",
|
||||
"EmailError": "邮箱 {0} 格式错误",
|
||||
"PhoneError": "手机号码 {0} 格式错误",
|
||||
"NoOrg": "组织机构不存在",
|
||||
"DirectorSelf": "不能设置自己为主管",
|
||||
|
||||
|
||||
"DemoCanotUpdatePassword": "DEMO环境不允许修改密码",
|
||||
"OldPasswordError": "原密码错误",
|
||||
"ConfirmPasswordDiff": "两次输入的密码不一致",
|
||||
"PasswordLengthLess": "密码长度不能小于 {0} ",
|
||||
"PasswordMustNum ": "密码必须包含数字",
|
||||
"PasswordMustLow": "密码必须包含小写字母",
|
||||
"PasswordMustUpp": "密码必须包含大写字母",
|
||||
"PasswordMustSpecial": "密码必须包含特殊字符"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysRole": {
|
||||
"Code": "编码",
|
||||
"Name": "名称",
|
||||
"Name.Required": " {0} 是必填项",
|
||||
"Category": "分类",
|
||||
"SortCode": "排序",
|
||||
"Global": "全局",
|
||||
"Status": "状态",
|
||||
"OrgId": "机构",
|
||||
"CreateTime": "创建时间",
|
||||
"UpdateTime": "更新时间",
|
||||
|
||||
"CanotDeleteAdmin": "不可删除系统内置超管角色",
|
||||
"CanotEditAdmin": "不可编辑超管角色",
|
||||
"CanotGrantAdmin": "不能分配超管角色",
|
||||
|
||||
"NameDup": "存在重复的角色名称 {0}",
|
||||
"OrgNotNull": "机构不能为空",
|
||||
"SameOrgNameDup": "存在重复的角色名称 {0}",
|
||||
"CannotRoleScopeAll": "机构角色不能选择全局数据范围",
|
||||
"CodeDup": "存在重复的编码 {0}"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
|
||||
"Global": "全局",
|
||||
"Org": "机构"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DataScopeEnum": {
|
||||
"SCOPE_SELF": "仅自己",
|
||||
"SCOPE_ALL": "全部",
|
||||
"SCOPE_ORG": "仅所属组织",
|
||||
"SCOPE_ORG_CHILD": "所属组织及以下",
|
||||
"SCOPE_ORG_DEFINE": "自定义"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DefaultDataScope": {
|
||||
"ScopeCategory": "数据范围",
|
||||
"ScopeDefineOrgIdList": "自定义列表"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysResource": {
|
||||
"Title": "标题",
|
||||
"Module": "模块",
|
||||
"Title.Required": "{0} 是必填项",
|
||||
"Href.Required": "{0} 是必填项",
|
||||
"Icon": "图标",
|
||||
"Href": "路径",
|
||||
"Code": "编码",
|
||||
"Category": "分类",
|
||||
"Target": "跳转类型",
|
||||
"NavLinkMatch": "匹配类型",
|
||||
"SortCode": "排序",
|
||||
"ParentId": "上级菜单",
|
||||
"CreateTime": "创建时间",
|
||||
"UpdateTime": "更新时间",
|
||||
"ResourceDup": "存在重复的名称 {0}",
|
||||
"ResourceParentChoiceSelf": "父级不能选择自己",
|
||||
"ResourceParentNull": "父级不存在 {0}",
|
||||
"NotFoundResource": "系统异常,没找到该菜单",
|
||||
"ModuleIdDiff": "模块与上级菜单不一致",
|
||||
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
|
||||
"ResourceMenuHrefNotNull": "菜单的路径不能为空"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
|
||||
"TargetId": "目标机构",
|
||||
"ContainsChild": "包含下级",
|
||||
"ContainsPosition": "包含职位"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysPosition": {
|
||||
"Category.Required": "{0} 是必填项",
|
||||
"Name.Required": "{0} 是必填项",
|
||||
"Code.Required": "{0} 是必填项",
|
||||
"OrgId.MinValue": "{0} 是必填项",
|
||||
"Category": "分类",
|
||||
"Name": "名称",
|
||||
"Code": "代码",
|
||||
"Status": "状态",
|
||||
"OrgId": "机构",
|
||||
"Remark": "备注",
|
||||
"SortCode": "排序",
|
||||
"CreateTime": "创建时间",
|
||||
"UpdateTime": "更新时间",
|
||||
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
|
||||
"CodeDup": "存在重复的编码 {0}",
|
||||
"NameDup": "存在重复的名称 {0}",
|
||||
"CanotContainsSelf": "不可包含自己",
|
||||
"TargetNameDup": "目标节点存在重复的名称 {0}",
|
||||
"ParentChoiceSelf": "父级不能选择自己",
|
||||
"ParentNull": "父级不存在 {0}",
|
||||
"DeleteUserFirst": "请先删除职位下的用户"
|
||||
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysOrg": {
|
||||
"Category.Required": "{0} 是必填项",
|
||||
"Name.Required": "{0} 是必填项",
|
||||
"Code.Required": "{0} 是必填项",
|
||||
"Category": "分类",
|
||||
"Name": "名称",
|
||||
"Code": "代码",
|
||||
"Status": "状态",
|
||||
"ParentId": "上级机构",
|
||||
"Names": "机构全称",
|
||||
"Remark": "备注",
|
||||
"DirectorId": "主管",
|
||||
"SortCode": "排序",
|
||||
"CreateTime": "创建时间",
|
||||
"UpdateTime": "更新时间",
|
||||
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
|
||||
"CodeDup": "存在重复的编码 {0}",
|
||||
"NameDup": "存在重复的名称 {0}",
|
||||
"CanotContainsSelf": "不可包含自己",
|
||||
"TargetNameDup": "目标节点存在重复的名称 {0}",
|
||||
"ParentChoiceSelf": "父级不能选择自己",
|
||||
"ParentNull": "父级不存在 {0}",
|
||||
"DeleteUserFirst": "请先删除机构下的用户",
|
||||
"DeleteRoleFirst": "请先删除机构下的角色",
|
||||
"DeletePositionFirst": "请先删除机构下的职位",
|
||||
"RootOrg": "无法创建顶层机构"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OrgEnum": {
|
||||
"COMPANY": "公司",
|
||||
"DEPT": "部门"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
|
||||
"HIGH": "高层",
|
||||
"MIDDLE": "中层",
|
||||
"LOW": "低层"
|
||||
},
|
||||
|
||||
//controller
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
//auth
|
||||
"AuthController": "登录API",
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
//auth
|
||||
"TestController": "测试API",
|
||||
"Test": "测试"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
//auth
|
||||
"OpenApiAuthController": "登录API",
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
"ThingsGateway.Admin.Application.FileService": {
|
||||
"FileNullError": "文件不能为空",
|
||||
"FileLengthError": "文件大小不允许超过 {0} M",
|
||||
"FileTypeError": "不支持 {0} 格式"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
||||
"TokenOver": "登录已过期,请重新登录",
|
||||
"NoPermission": "禁止访问,没有权限"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
"TenantNull": "租户不存在",
|
||||
"OrgDisable": "所属公司/部门已停用,请联系管理员",
|
||||
"SingleLoginWarn": "您的账号已在别处登录",
|
||||
"UserNull": "用户 {0} 不存在",
|
||||
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
|
||||
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
|
||||
"UserDisable": "账号 {0} 已停用",
|
||||
"MustDesc": "密码需要DESC加密后传入",
|
||||
"UserNoModule": "该账号未分配模块,请联系管理员"
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||
"Environment": "主机环境",
|
||||
"FrameworkDescription": "NET框架",
|
||||
"OsArchitecture": "系统架构",
|
||||
"UUID": "唯一编码",
|
||||
"UpdateTime": "更新时间"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
|
||||
"DriveUsage": "磁盘使用率",
|
||||
"MemoryUsage": "内存",
|
||||
"CpuUsage": "CPU使用率",
|
||||
"Temperature": "温度",
|
||||
"Battery": "电池"
|
||||
},
|
||||
|
||||
//oper
|
||||
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
||||
//dict
|
||||
"SaveDict": "修改字典",
|
||||
"DeleteDict": "删除字典",
|
||||
"EditLoginPolicy": "修改登录策略",
|
||||
"EditPasswordPolicy": "修改密码策略",
|
||||
"EditPagePolicy": "修改页面策略",
|
||||
"EditWebsitePolicy": "修改网站设置",
|
||||
//operlog
|
||||
"DeleteOperLog": "删除操作日志",
|
||||
"ExportOperLog": "导出操作日志",
|
||||
|
||||
//resource
|
||||
"SaveResource": "修改资源",
|
||||
"DeleteResource": "删除资源",
|
||||
|
||||
//role
|
||||
"SaveRole": "修改角色",
|
||||
"DeleteRole": "删除角色",
|
||||
"RoleGrantResource": "角色授权资源",
|
||||
"RoleGrantUser": "角色授权用户",
|
||||
"RoleGrantApiPermission": "角色授权OpenApi",
|
||||
"GrantApi": "API",
|
||||
"GrantUser": "用户",
|
||||
"GrantRole": "角色",
|
||||
"GrantResource": "资源",
|
||||
//user
|
||||
"SaveUser": "修改用户",
|
||||
"DeleteuSER": "删除用户",
|
||||
"ResetPassword": "重置密码",
|
||||
"UserGrantRole": "用户授权角色",
|
||||
"UserGrantResource": "用户授权资源",
|
||||
"UserGrantApiPermission": "用户授权OpenApi",
|
||||
|
||||
//usercenter
|
||||
"UpdateUserInfo": "更新个人信息",
|
||||
"WorkbenchInfo": "更新个人工作台",
|
||||
"UpdatePassword": "更新个人密码",
|
||||
|
||||
//session
|
||||
"ExitVerificat": "强退令牌",
|
||||
"ExitSession": "强退会话",
|
||||
|
||||
|
||||
"CopyOrg": "复制机构",
|
||||
"DeleteOrg": "删除机构",
|
||||
"SaveOrg": "保存机构",
|
||||
|
||||
"DeletePosition": "删除岗位",
|
||||
"SavePosition": "保存岗位",
|
||||
|
||||
"NoPermission": "无权限操作",
|
||||
|
||||
"CopyResource": "复制资源",
|
||||
"ChangeParentResource": "更改父节点"
|
||||
},
|
||||
|
||||
//service
|
||||
|
||||
"ThingsGateway.Admin.Application.HardwareJob": {
|
||||
"GetHardwareInfoFail": "获取硬件信息出错"
|
||||
},
|
||||
|
||||
//dto
|
||||
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
||||
"Account": "账号",
|
||||
"OrgId": "机构"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
|
||||
"Module": "模块",
|
||||
"Href": "路径",
|
||||
"Title": "标题"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WorkbenchInfo": {
|
||||
"Razor": "主页",
|
||||
"Shortcuts": "快捷方式"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
|
||||
"Password": "密码",
|
||||
"NewPassword": "新密码",
|
||||
"ConfirmPassword": "确认密码",
|
||||
"Password.Required": " {0} 是必填项",
|
||||
"NewPassword.Required": " {0} 是必填项",
|
||||
"ConfirmPassword.Required": " {0} 是必填项"
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.Admin.Application.VerificatInfo": {
|
||||
"Expire": "过期时间(分)",
|
||||
"Online": "在线状态",
|
||||
"VerificatTimeout": "超时时间",
|
||||
"Device": "登录设备",
|
||||
"LoginIp": "登录IP",
|
||||
"LoginTime": "登录时间"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SessionOutput": {
|
||||
"Account": "账号",
|
||||
"Online": "在线状态",
|
||||
"LatestLoginIp": "最新登录ip",
|
||||
"LatestLoginTime": "最新登录时间",
|
||||
"VerificatCount": "令牌数量"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.SysDict": {
|
||||
"Category.Required": "{0} 是必填项",
|
||||
"Name.Required": "{0} 是必填项",
|
||||
"Code.Required": "{0} 是必填项",
|
||||
"Category": "分类",
|
||||
"Name": "名称",
|
||||
"Code": "代码",
|
||||
"Remark": "备注",
|
||||
"SortCode": "排序",
|
||||
"CreateTime": "创建时间",
|
||||
"UpdateTime": "更新时间",
|
||||
"DictDup": "存在重复的配置 分类 {0} 名称 {1}",
|
||||
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置"
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.Admin.Application.SysOperateLog": {
|
||||
"ClassName": "类名",
|
||||
"ExeMessage": "具体消息",
|
||||
"MethodName": "方法名称",
|
||||
"ParamJson": "请求参数",
|
||||
"ReqMethod": "请求方式",
|
||||
"ReqUrl": "请求地址",
|
||||
"ResultJson": "返回结果",
|
||||
"Category": "日志分类",
|
||||
"ExeStatus": "执行状态",
|
||||
"Name": "日志名称",
|
||||
"OpAccount": "账号",
|
||||
"OpBrowser": "浏览器",
|
||||
"OpIp": "ip",
|
||||
"OpOs": "系统",
|
||||
"OpTime": "操作时间",
|
||||
"VerificatId": "验证Id"
|
||||
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OperateLogPageInput": {
|
||||
"SearchDate": "时间范围",
|
||||
"Account": "操作账号",
|
||||
"Category": "分类"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginInput": {
|
||||
"Account": "登录账号",
|
||||
"Password": "登录密码",
|
||||
"Account.Required": "{0} 是必填项",
|
||||
"Password.Required": "{0} 是必填项"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogoutInput": {
|
||||
"VerificatId.Required": "{0} 是必填项"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AppConfig": {
|
||||
"LoginPolicy": "登录策略",
|
||||
"PasswordPolicy": "密码策略",
|
||||
"PagePolicy": "页面设置",
|
||||
"WebsitePolicy": "网站设置"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginPolicy": {
|
||||
"SingleOpen": "单用户登录开关",
|
||||
"ErrorLockTime": "登录错误锁定时长(分)",
|
||||
"ErrorResetTime": "登录错误次数过期时长(分)",
|
||||
"ErrorCount": "登录错误次数锁定阈值",
|
||||
"VerificatExpireTime": "登录过期时间(分)",
|
||||
"ErrorLockTime.MinValue": " {0} 值太小",
|
||||
"ErrorResetTime.MinValue": " {0} 值太小",
|
||||
"ErrorCount.MinValue": " {0} 值太小",
|
||||
"VerificatExpireTime.MinValue": " {0} 值太小"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PagePolicy": {
|
||||
"Shortcuts": "默认快捷方式",
|
||||
"Razor": "默认主页"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PasswordPolicy": {
|
||||
"DefaultPassword": "默认用户密码",
|
||||
"DefaultPassword.Required": " {0} 是必填项",
|
||||
"PasswordMinLen": "密码最小长度",
|
||||
"PasswordMinLen.MinValue": " {0} 值太小",
|
||||
"PasswordContainNum": "包含数字",
|
||||
"PasswordContainLower": "包含小写字母",
|
||||
"PasswordContainUpper": "包含大写字母",
|
||||
"PasswordContainChar": "包含特殊字符"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WebsitePolicy": {
|
||||
"WebStatus": "是否开放",
|
||||
"CloseTip": "关闭提示",
|
||||
"CloseTip.Required": " {0} 是必填项"
|
||||
},
|
||||
|
||||
|
||||
|
||||
//enum
|
||||
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
|
||||
"Module": "模块",
|
||||
"Menu": "菜单",
|
||||
"Button": "按钮"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.TargetEnum": {
|
||||
"_self": "本窗口",
|
||||
"_blank": "新窗口",
|
||||
"_parent": "父级窗口",
|
||||
"_top": "顶级窗口"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DictTypeEnum": {
|
||||
"System": "系统配置",
|
||||
"Define": "业务配置"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
|
||||
"Login": "登录",
|
||||
"Logout": "注销",
|
||||
"Operate": "操作",
|
||||
"Exception": "异常"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.LogEnum": {
|
||||
"SUCCESS": "成功",
|
||||
"FAIL": "失败"
|
||||
}
|
||||
"UserExpire": "用户登录已过期,请重新登录"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DataScopeEnum": {
|
||||
"SCOPE_ALL": "全部",
|
||||
"SCOPE_ORG": "仅所属组织",
|
||||
"SCOPE_ORG_CHILD": "所属组织及以下",
|
||||
"SCOPE_ORG_DEFINE": "自定义",
|
||||
"SCOPE_SELF": "仅自己"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DefaultDataScope": {
|
||||
"ScopeCategory": "数据范围",
|
||||
"ScopeDefineOrgIdList": "自定义列表"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.DictTypeEnum": {
|
||||
"Define": "业务配置",
|
||||
"System": "系统配置"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.FileService": {
|
||||
"FileLengthError": "文件大小不允许超过 {0} M",
|
||||
"FileNullError": "文件不能为空",
|
||||
"FileTypeError": "不支持 {0} 格式"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.HardwareInfo": {
|
||||
"Environment": "主机环境",
|
||||
"FrameworkDescription": "NET框架",
|
||||
"OsArchitecture": "系统架构",
|
||||
"UpdateTime": "更新时间",
|
||||
"UUID": "唯一编码"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.HardwareJob": {
|
||||
"GetHardwareInfoFail": "获取硬件信息出错"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
|
||||
"Battery": "电池",
|
||||
"CpuUsage": "CPU使用率",
|
||||
"DriveUsage": "磁盘使用率",
|
||||
"MemoryUsage": "内存",
|
||||
"Temperature": "温度"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
|
||||
"Exception": "异常",
|
||||
"Login": "登录",
|
||||
"Logout": "注销",
|
||||
"Operate": "操作"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogEnum": {
|
||||
"FAIL": "失败",
|
||||
"SUCCESS": "成功"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginInput": {
|
||||
"Account": "登录账号",
|
||||
"Account.Required": "{0} 是必填项",
|
||||
"Password": "登录密码",
|
||||
"Password.Required": "{0} 是必填项"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LoginPolicy": {
|
||||
"ErrorCount": "登录错误次数锁定阈值",
|
||||
"ErrorCount.MinValue": " {0} 值太小",
|
||||
"ErrorLockTime": "登录错误锁定时长(分)",
|
||||
"ErrorLockTime.MinValue": " {0} 值太小",
|
||||
"ErrorResetTime": "登录错误次数过期时长(分)",
|
||||
"ErrorResetTime.MinValue": " {0} 值太小",
|
||||
"SingleOpen": "单用户登录开关",
|
||||
"VerificatExpireTime": "登录过期时间(分)",
|
||||
"VerificatExpireTime.MinValue": " {0} 值太小"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.LogoutInput": {
|
||||
"VerificatId.Required": "{0} 是必填项"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销",
|
||||
"OpenApiAuthController": "登录API"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OperateLogPageInput": {
|
||||
"Account": "操作账号",
|
||||
"Category": "分类",
|
||||
"SearchDate": "时间范围"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
||||
"ChangeParentResource": "更改父节点",
|
||||
"CopyOrg": "复制机构",
|
||||
"CopyResource": "复制资源",
|
||||
"DeleteDict": "删除字典",
|
||||
"DeleteOperLog": "删除操作日志",
|
||||
"DeleteOrg": "删除机构",
|
||||
"DeletePosition": "删除岗位",
|
||||
"DeleteResource": "删除资源",
|
||||
"DeleteRole": "删除角色",
|
||||
"DeleteuSER": "删除用户",
|
||||
"EditLoginPolicy": "修改登录策略",
|
||||
"EditPagePolicy": "修改页面策略",
|
||||
"EditPasswordPolicy": "修改密码策略",
|
||||
"EditWebsitePolicy": "修改网站设置",
|
||||
"ExitSession": "强退会话",
|
||||
"ExitVerificat": "强退令牌",
|
||||
"ExportOperLog": "导出操作日志",
|
||||
"GrantApi": "API",
|
||||
"GrantResource": "资源",
|
||||
"GrantRole": "角色",
|
||||
"GrantUser": "用户",
|
||||
"NoPermission": "无权限操作",
|
||||
"ResetPassword": "重置密码",
|
||||
"RoleGrantApiPermission": "角色授权OpenApi",
|
||||
"RoleGrantResource": "角色授权资源",
|
||||
"RoleGrantUser": "角色授权用户",
|
||||
"SaveDict": "修改字典",
|
||||
"SaveOrg": "保存机构",
|
||||
"SavePosition": "保存岗位",
|
||||
"SaveResource": "修改资源",
|
||||
"SaveRole": "修改角色",
|
||||
"SaveUser": "修改用户",
|
||||
"UpdatePassword": "更新个人密码",
|
||||
"UpdateUserInfo": "更新个人信息",
|
||||
"UserGrantApiPermission": "用户授权OpenApi",
|
||||
"UserGrantResource": "用户授权资源",
|
||||
"UserGrantRole": "用户授权角色",
|
||||
"WorkbenchInfo": "更新个人工作台"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OrgEnum": {
|
||||
"COMPANY": "公司",
|
||||
"DEPT": "部门"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PagePolicy": {
|
||||
"Razor": "默认主页",
|
||||
"Shortcuts": "默认快捷方式"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PasswordPolicy": {
|
||||
"DefaultPassword": "默认用户密码",
|
||||
"DefaultPassword.Required": " {0} 是必填项",
|
||||
"PasswordContainChar": "包含特殊字符",
|
||||
"PasswordContainLower": "包含小写字母",
|
||||
"PasswordContainNum": "包含数字",
|
||||
"PasswordContainUpper": "包含大写字母",
|
||||
"PasswordMinLen": "密码最小长度",
|
||||
"PasswordMinLen.MinValue": " {0} 值太小"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
|
||||
"HIGH": "高层",
|
||||
"LOW": "低层",
|
||||
"MIDDLE": "中层"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
|
||||
"Button": "按钮",
|
||||
"Menu": "菜单",
|
||||
"Module": "模块"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
|
||||
"Href": "路径",
|
||||
"Module": "模块",
|
||||
"Title": "标题"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
|
||||
"Global": "全局",
|
||||
"Org": "机构"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SessionOutput": {
|
||||
"Account": "账号",
|
||||
"LatestLoginIp": "最新登录ip",
|
||||
"LatestLoginTime": "最新登录时间",
|
||||
"Online": "在线状态",
|
||||
"VerificatCount": "令牌数量"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysDict": {
|
||||
"Category": "分类",
|
||||
"Category.Required": "{0} 是必填项",
|
||||
"Code": "代码",
|
||||
"Code.Required": "{0} 是必填项",
|
||||
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置",
|
||||
"DictDup": "存在重复的配置 分类 {0} 名称 {1}",
|
||||
"Name": "名称",
|
||||
"Name.Required": "{0} 是必填项",
|
||||
"Remark": "备注"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysOperateLog": {
|
||||
"Category": "日志分类",
|
||||
"ClassName": "类名",
|
||||
"ExeMessage": "具体消息",
|
||||
"ExeStatus": "执行状态",
|
||||
"MethodName": "方法名称",
|
||||
"Name": "日志名称",
|
||||
"OpAccount": "账号",
|
||||
"OpBrowser": "浏览器",
|
||||
"OpIp": "ip",
|
||||
"OpOs": "系统",
|
||||
"OpTime": "操作时间",
|
||||
"ParamJson": "请求参数",
|
||||
"ReqMethod": "请求方式",
|
||||
"ReqUrl": "请求地址",
|
||||
"ResultJson": "返回结果",
|
||||
"VerificatId": "验证Id"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysOrg": {
|
||||
"CanotContainsSelf": "不可包含自己",
|
||||
"Category": "分类",
|
||||
"Category.Required": "{0} 是必填项",
|
||||
"Code": "代码",
|
||||
"Code.Required": "{0} 是必填项",
|
||||
"CodeDup": "存在重复的编码 {0}",
|
||||
"DeletePositionFirst": "请先删除机构下的职位",
|
||||
"DeleteRoleFirst": "请先删除机构下的角色",
|
||||
"DeleteUserFirst": "请先删除机构下的用户",
|
||||
"DirectorId": "主管",
|
||||
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
|
||||
"Name": "名称",
|
||||
"Name.Required": "{0} 是必填项",
|
||||
"NameDup": "存在重复的名称 {0}",
|
||||
"Names": "机构全称",
|
||||
"ParentChoiceSelf": "父级不能选择自己",
|
||||
"ParentId": "上级机构",
|
||||
"ParentNull": "父级不存在 {0}",
|
||||
"Remark": "备注",
|
||||
"RootOrg": "无法创建顶层机构",
|
||||
"Status": "状态",
|
||||
"TargetNameDup": "目标节点存在重复的名称 {0}"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
|
||||
"ContainsChild": "包含下级",
|
||||
"ContainsPosition": "包含职位",
|
||||
"TargetId": "目标机构"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysPosition": {
|
||||
"CanotContainsSelf": "不可包含自己",
|
||||
"Category": "分类",
|
||||
"Category.Required": "{0} 是必填项",
|
||||
"Code": "代码",
|
||||
"Code.Required": "{0} 是必填项",
|
||||
"CodeDup": "存在重复的编码 {0}",
|
||||
"DeleteUserFirst": "请先删除职位下的用户",
|
||||
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
|
||||
"Name": "名称",
|
||||
"Name.Required": "{0} 是必填项",
|
||||
"NameDup": "存在重复的名称 {0}",
|
||||
"OrgId": "机构",
|
||||
"OrgId.MinValue": "{0} 是必填项",
|
||||
"ParentChoiceSelf": "父级不能选择自己",
|
||||
"ParentNull": "父级不存在 {0}",
|
||||
"Remark": "备注",
|
||||
"Status": "状态",
|
||||
"TargetNameDup": "目标节点存在重复的名称 {0}"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysResource": {
|
||||
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
|
||||
"Category": "分类",
|
||||
"Code": "编码",
|
||||
"Href": "路径",
|
||||
"Href.Required": "{0} 是必填项",
|
||||
"Icon": "图标",
|
||||
"Module": "模块",
|
||||
"ModuleIdDiff": "模块与上级菜单不一致",
|
||||
"NavLinkMatch": "匹配类型",
|
||||
"NotFoundResource": "系统异常,没找到该菜单",
|
||||
"ParentId": "上级菜单",
|
||||
"ResourceDup": "存在重复的名称 {0}",
|
||||
"ResourceMenuHrefNotNull": "菜单的路径不能为空",
|
||||
"ResourceParentChoiceSelf": "父级不能选择自己",
|
||||
"ResourceParentNull": "父级不存在 {0}",
|
||||
"Target": "跳转类型",
|
||||
"Title": "标题",
|
||||
"Title.Required": "{0} 是必填项"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysRole": {
|
||||
"CannotRoleScopeAll": "机构角色不能选择全局数据范围",
|
||||
"CanotDeleteAdmin": "不可删除系统内置超管角色",
|
||||
"CanotEditAdmin": "不可编辑超管角色",
|
||||
"CanotGrantAdmin": "不能分配超管角色",
|
||||
"Category": "分类",
|
||||
"Code": "编码",
|
||||
"CodeDup": "存在重复的编码 {0}",
|
||||
"Global": "全局",
|
||||
"Name": "名称",
|
||||
"Name.Required": " {0} 是必填项",
|
||||
"NameDup": "存在重复的角色名称 {0}",
|
||||
"OrgId": "机构",
|
||||
"OrgNotNull": "机构不能为空",
|
||||
"SameOrgNameDup": "存在重复的角色名称 {0}",
|
||||
"Status": "状态"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.SysUser": {
|
||||
"Account": "账号",
|
||||
"Account.Required": " {0} 是必填项",
|
||||
"AccountDup": "存在重复的账号 {0}",
|
||||
"Avatar": "头像",
|
||||
"CanotDeleteAdminUser": "不可删除系统内置超管用户",
|
||||
"CanotDeleteSelf": "不可删除自己",
|
||||
"CanotEditAdminUser": "不可编辑超管用户",
|
||||
"CanotGrantAdmin": "不能分配超管角色",
|
||||
"CheckSelf": "禁止 {0} 自己",
|
||||
"ConfirmPasswordDiff": "两次输入的密码不一致",
|
||||
"DemoCanotUpdatePassword": "DEMO环境不允许修改密码",
|
||||
"DirectorId": "主管",
|
||||
"DirectorSelf": "不能设置自己为主管",
|
||||
"Disable": "禁用",
|
||||
"Email": "邮箱",
|
||||
"EmailDup": "存在重复的邮箱 {0}",
|
||||
"EmailError": "邮箱 {0} 格式错误",
|
||||
"Enable": "启用",
|
||||
"ExitVerificat": "您已被强制下线",
|
||||
"GrantRole": "分配角色",
|
||||
"LastLoginAddress": "上次登录地点",
|
||||
"LastLoginDevice": "上次登录设备",
|
||||
"LastLoginIp": "上次登录ip",
|
||||
"LastLoginTime": "上次登录时间",
|
||||
"LatestLoginAddress": "最新登录地点",
|
||||
"LatestLoginDevice": "最新登录设备",
|
||||
"LatestLoginIp": "最新登录ip",
|
||||
"LatestLoginTime": "最新登录时间",
|
||||
"NoOrg": "组织机构不存在",
|
||||
"OldPasswordError": "原密码错误",
|
||||
"OrgId": "机构",
|
||||
"OrgNames": "机构名称",
|
||||
"Password": "密码",
|
||||
"PasswordEdited": "密码被修改,已退出登录",
|
||||
"PasswordLengthLess": "密码长度不能小于 {0} ",
|
||||
"PasswordMustLow": "密码必须包含小写字母",
|
||||
"PasswordMustNum": "密码必须包含数字",
|
||||
"PasswordMustSpecial": "密码必须包含特殊字符",
|
||||
"PasswordMustUpp": "密码必须包含大写字母",
|
||||
"Phone": "手机",
|
||||
"PhoneError": "手机号码 {0} 格式错误",
|
||||
"PositionId": "职位",
|
||||
"PositionName": "职位名称",
|
||||
"Status": "状态"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TargetEnum": {
|
||||
"_blank": "新窗口",
|
||||
"_parent": "父级窗口",
|
||||
"_self": "本窗口",
|
||||
"_top": "顶级窗口"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
"Test": "测试",
|
||||
"TestController": "测试API"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
||||
"NoPermission": "禁止访问,没有权限",
|
||||
"TokenOver": "登录已过期,请重新登录"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
|
||||
"ConfirmPassword": "确认密码",
|
||||
"ConfirmPassword.Required": " {0} 是必填项",
|
||||
"NewPassword": "新密码",
|
||||
"NewPassword.Required": " {0} 是必填项",
|
||||
"Password": "密码",
|
||||
"Password.Required": " {0} 是必填项"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
||||
"Account": "账号",
|
||||
"OrgId": "机构"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.VerificatInfo": {
|
||||
"Device": "登录设备",
|
||||
"Expire": "过期时间(分)",
|
||||
"LoginIp": "登录IP",
|
||||
"LoginTime": "登录时间",
|
||||
"Online": "在线状态",
|
||||
"VerificatTimeout": "超时时间"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WebsitePolicy": {
|
||||
"CloseTip": "关闭提示",
|
||||
"CloseTip.Required": " {0} 是必填项",
|
||||
"WebStatus": "是否开放"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.WorkbenchInfo": {
|
||||
"Razor": "主页",
|
||||
"Shortcuts": "快捷方式"
|
||||
}
|
||||
}
|
@@ -8,17 +8,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Extension;
|
||||
using ThingsGateway.FriendlyException;
|
||||
using ThingsGateway.Logging;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Razor;
|
||||
|
||||
using UAParser;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -41,33 +39,36 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
||||
/// <param name="flush"></param>
|
||||
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;
|
||||
// loggingMonitor.ReturnInformation.Value
|
||||
requestAuditData.LogDateTime = logMsg.LogDateTime;
|
||||
// requestAuditData.ReturnInformation.Value
|
||||
//验证失败不记录日志
|
||||
bool save = false;
|
||||
if (loggingMonitor.Validation == null)
|
||||
if (requestAuditData.Validation == null)
|
||||
{
|
||||
var operation = logMsg.Context.Get(LoggingConst.Operation).ToString();//获取操作名称
|
||||
var client = (ClientInfo)logMsg.Context.Get(LoggingConst.Client);//获取客户端信息
|
||||
var path = logMsg.Context.Get(LoggingConst.Path).ToString();//获取操作名称
|
||||
var method = logMsg.Context.Get(LoggingConst.Method).ToString();//获取方法
|
||||
var operation = requestAuditData.Operation;//获取操作名称
|
||||
var client = requestAuditData.Client;//获取客户端信息
|
||||
var path = requestAuditData.Path;//获取操作名称
|
||||
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
|
||||
{
|
||||
//添加到异常日志
|
||||
save = await CreateOperationLog(operation, path, loggingMonitor, client, flush).ConfigureAwait(false);
|
||||
save = await CreateOperationLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -76,7 +77,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
||||
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 +92,21 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
||||
/// </summary>
|
||||
/// <param name="operation">操作名称</param>
|
||||
/// <param name="path">请求地址</param>
|
||||
/// <param name="loggingMonitor">loggingMonitor</param>
|
||||
/// <param name="clientInfo">客户端信息</param>
|
||||
/// <param name="requestAuditData">requestAuditData</param>
|
||||
/// <param name="userAgent">客户端信息</param>
|
||||
/// <param name="flush"></param>
|
||||
/// <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字符串,
|
||||
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字符串
|
||||
var resultJson = string.Empty;
|
||||
if (loggingMonitor.ReturnInformation != null)//如果有返回值
|
||||
{
|
||||
if (loggingMonitor.ReturnInformation.Value != null)//如果返回值不为空
|
||||
{
|
||||
resultJson = loggingMonitor.ReturnInformation.Value.ToJsonNetString();
|
||||
}
|
||||
}
|
||||
var resultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString();
|
||||
|
||||
|
||||
//操作日志表实体
|
||||
var sysLogOperate = new SysOperateLog
|
||||
@@ -119,29 +114,29 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
||||
Name = operation,
|
||||
Category = LogCateGoryEnum.Operate,
|
||||
ExeStatus = true,
|
||||
OpIp = loggingMonitor.RemoteIPv4,
|
||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
||||
OpTime = loggingMonitor.LogDateTime.LocalDateTime,
|
||||
OpIp = requestAuditData.RemoteIPv4,
|
||||
OpBrowser = userAgent?.Browser,
|
||||
OpOs = userAgent?.Platform,
|
||||
OpTime = requestAuditData.LogDateTime.LocalDateTime,
|
||||
OpAccount = opAccount,
|
||||
ReqMethod = loggingMonitor.HttpMethod,
|
||||
ReqMethod = requestAuditData.Method,
|
||||
ReqUrl = path,
|
||||
ResultJson = resultJson,
|
||||
ClassName = loggingMonitor.DisplayName,
|
||||
MethodName = loggingMonitor.ActionName,
|
||||
ClassName = requestAuditData.ControllerName,
|
||||
MethodName = requestAuditData.ActionName,
|
||||
ParamJson = paramJson,
|
||||
VerificatId = UserManager.VerificatId,
|
||||
};
|
||||
//如果异常不为空
|
||||
if (loggingMonitor.Exception != null)
|
||||
if (requestAuditData.Exception != null)
|
||||
{
|
||||
sysLogOperate.Category = LogCateGoryEnum.Exception;//操作类型为异常
|
||||
sysLogOperate.ExeStatus = false;//操作状态为失败
|
||||
|
||||
if (loggingMonitor.Exception.Type == typeof(AppFriendlyException).ToString())
|
||||
sysLogOperate.ExeMessage = loggingMonitor?.Exception.Message;
|
||||
if (requestAuditData.Exception.Type == typeof(AppFriendlyException).ToString())
|
||||
sysLogOperate.ExeMessage = requestAuditData?.Exception.Message;
|
||||
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);
|
||||
@@ -160,46 +155,48 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
|
||||
/// </summary>
|
||||
/// <param name="operation">访问类型</param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="loggingMonitor">loggingMonitor</param>
|
||||
/// <param name="clientInfo">客户端信息</param>
|
||||
/// <param name="requestAuditData">requestAuditData</param>
|
||||
/// <param name="userAgent">客户端信息</param>
|
||||
/// <param name="logCateGoryEnum">logCateGory</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
|
||||
var opAccount = "";//用户账号
|
||||
if (path == "/api/auth/login")
|
||||
if (logCateGoryEnum == LogCateGoryEnum.Login)
|
||||
{
|
||||
//如果是登录,用户信息就从返回值里拿
|
||||
var result = loggingMonitor.ReturnInformation?.Value?.ToJsonNetString();//返回值转json
|
||||
var userInfo = result.FromJsonNetString<UnifyResult<LoginOutput>>();//格式化成user表
|
||||
opAccount = userInfo.Data.Account;//赋值账号
|
||||
verificatId = userInfo.Data.VerificatId;
|
||||
if (requestAuditData.ReturnInformation is UnifyResult<LoginOutput> userInfo)
|
||||
{
|
||||
opAccount = userInfo.Data.Account;//赋值账号
|
||||
verificatId = userInfo.Data.VerificatId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//如果是登录出,用户信息就从AuthorizationClaims里拿
|
||||
opAccount = loggingMonitor.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();
|
||||
opAccount = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault();
|
||||
verificatId = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.VerificatId).Select(it => it.Value).FirstOrDefault().ToLong();
|
||||
}
|
||||
//日志表实体
|
||||
var sysLogVisit = new SysOperateLog
|
||||
{
|
||||
Name = operation,
|
||||
Category = path == "/api/auth/login" ? LogCateGoryEnum.Login : LogCateGoryEnum.Logout,
|
||||
Category = logCateGoryEnum,
|
||||
ExeStatus = true,
|
||||
OpIp = loggingMonitor.RemoteIPv4,
|
||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
||||
OpTime = loggingMonitor.LogDateTime.LocalDateTime,
|
||||
OpIp = requestAuditData.RemoteIPv4,
|
||||
OpBrowser = userAgent?.Browser,
|
||||
OpOs = userAgent?.Platform,
|
||||
OpTime = requestAuditData.LogDateTime.LocalDateTime,
|
||||
VerificatId = verificatId,
|
||||
OpAccount = opAccount,
|
||||
|
||||
ReqMethod = loggingMonitor.HttpMethod,
|
||||
ReqMethod = requestAuditData.Method,
|
||||
ReqUrl = path,
|
||||
ResultJson = loggingMonitor.ReturnInformation?.Value?.ToJsonNetString(),
|
||||
ClassName = loggingMonitor.DisplayName,
|
||||
MethodName = loggingMonitor.ActionName,
|
||||
ParamJson = loggingMonitor.Parameters?.ToJsonNetString(),
|
||||
ResultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString(),
|
||||
ClassName = requestAuditData.ControllerName,
|
||||
MethodName = requestAuditData.ActionName,
|
||||
ParamJson = requestAuditData.Parameters?.ToSystemTextJsonString(),
|
||||
};
|
||||
_operateLogMessageQueue.Enqueue(sysLogVisit);
|
||||
|
||||
|
@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// <summary>
|
||||
/// 日志常量
|
||||
/// </summary>
|
||||
public class LoggingConst
|
||||
public static class LoggingConst
|
||||
{
|
||||
/// <summary>
|
||||
/// 分类
|
||||
|
@@ -87,7 +87,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler
|
||||
var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false);
|
||||
|
||||
//这里鉴别用户使能状态
|
||||
if (user == null || !user.Status)
|
||||
if (user?.Status != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler
|
||||
else
|
||||
{
|
||||
//这里鉴别用户使能状态
|
||||
if (user == null || !user.Status)
|
||||
if (user?.Status != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@@ -22,26 +22,19 @@ using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Extension;
|
||||
using ThingsGateway.SpecificationDocument;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
internal sealed class ApiPermissionService : IApiPermissionService
|
||||
{
|
||||
private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
|
||||
private readonly SwaggerGeneratorOptions _generatorOptions;
|
||||
|
||||
public ApiPermissionService(
|
||||
IOptions<SwaggerGeneratorOptions> generatorOptions,
|
||||
IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider)
|
||||
{
|
||||
_generatorOptions = generatorOptions.Value;
|
||||
_apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
|
||||
}
|
||||
private IEnumerable<string> GetDocumentNames()
|
||||
{
|
||||
return _generatorOptions.SwaggerDocs.Keys;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector()
|
||||
@@ -53,37 +46,37 @@ internal sealed class ApiPermissionService : IApiPermissionService
|
||||
permissions = 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;
|
||||
|
||||
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 =
|
||||
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)
|
||||
{
|
||||
|
||||
|
||||
var routes = apiDescriptionGroup.Items.Where(api => api.ActionDescriptor is ControllerActionDescriptor);
|
||||
|
||||
OpenApiPermissionTreeSelector openApiPermissionTreeSelector = groupOpenApi.Value;
|
||||
|
||||
Dictionary<string, OpenApiPermissionTreeSelector> openApiPermissionTreeSelectorDict = new();
|
||||
|
||||
foreach (var route in routes)
|
||||
{
|
||||
if (!SpecificationDocumentBuilder.CheckApiDescriptionInCurrentGroup(groupOpenApi.Key, route))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var actionDesc = (ControllerActionDescriptor)route.ActionDescriptor;
|
||||
if (!actionDesc.ControllerTypeInfo.CustomAttributes.Any(a => a.AttributeType == typeof(RolePermissionAttribute)))
|
||||
continue;
|
||||
@@ -116,10 +109,8 @@ internal sealed class ApiPermissionService : IApiPermissionService
|
||||
}
|
||||
|
||||
|
||||
openApiPermissionTreeSelector.Children.AddRange(openApiPermissionTreeSelectorDict.Values);
|
||||
|
||||
if (openApiPermissionTreeSelector.Children.Any(a => a.Children.Count > 0))
|
||||
permissions.Add(openApiPermissionTreeSelector);
|
||||
if (openApiPermissionTreeSelectorDict.Values.Any(a => a.Children.Count > 0))
|
||||
permissions.AddRange(openApiPermissionTreeSelectorDict.Values);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -15,12 +15,17 @@ using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
using System.Security.Claims;
|
||||
|
||||
using UAParser;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
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)
|
||||
{
|
||||
var url = QueryHelpers.AddQueryString(CookieAuthenticationDefaults.LoginPath, new Dictionary<string, string?>
|
||||
@@ -41,18 +46,16 @@ public class AppService : IAppService
|
||||
{
|
||||
}
|
||||
}
|
||||
public Parser Parser = Parser.GetDefault();
|
||||
public ClientInfo? ClientInfo
|
||||
public UserAgent? UserAgent
|
||||
{
|
||||
get
|
||||
{
|
||||
var str = App.HttpContext?.Request?.Headers?.UserAgent;
|
||||
ClientInfo? clientInfo = null;
|
||||
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,
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
public ClaimsPrincipal? User => App.User;
|
||||
public ClaimsPrincipal? User => ClaimsPrincipalService.User;
|
||||
|
||||
public string? RemoteIpAddress => App.HttpContext?.GetRemoteIpAddressToIPv4();
|
||||
|
||||
|
@@ -13,19 +13,17 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using System.Security.Claims;
|
||||
|
||||
using UAParser;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
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";
|
||||
ClientInfo = Parser.GetDefault().Parse(str);
|
||||
UserAgent = userAgentService.Parse(str);
|
||||
RemoteIpAddress = "127.0.0.1";
|
||||
}
|
||||
public ClientInfo? ClientInfo { get; }
|
||||
public UserAgent? UserAgent { get; }
|
||||
|
||||
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 UAParser;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public interface IAppService
|
||||
@@ -20,7 +18,7 @@ public interface IAppService
|
||||
/// <summary>
|
||||
/// ClientInfo
|
||||
/// </summary>
|
||||
public ClientInfo? ClientInfo { get; }
|
||||
public UserAgent? UserAgent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ClaimsPrincipal
|
||||
|
@@ -12,8 +12,6 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Security.Claims;
|
||||
|
||||
using ThingsGateway.DataEncryption;
|
||||
@@ -64,6 +62,10 @@ public class AuthService : IAuthService
|
||||
{
|
||||
throw Oops.Bah(appConfig.WebsitePolicy.CloseTip);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
string? password = input.Password;
|
||||
if (isCookie) //openApi登录不再需要解密
|
||||
{
|
||||
@@ -96,9 +98,9 @@ public class AuthService : IAuthService
|
||||
/// </summary>
|
||||
public async Task LoginOutAsync()
|
||||
{
|
||||
if (UserManager.UserId == 0)
|
||||
if (UserManager.VerificatId == 0)
|
||||
return;
|
||||
var verificatId = UserManager.UserId;
|
||||
var verificatId = UserManager.VerificatId;
|
||||
//获取用户信息
|
||||
var userinfo = await _sysUserService.GetUserByAccountAsync(UserManager.UserAccount, UserManager.TenantId).ConfigureAwait(false);
|
||||
if (userinfo != null)
|
||||
@@ -237,25 +239,20 @@ public class AuthService : IAuthService
|
||||
var logingEvent = new LoginEvent
|
||||
{
|
||||
Ip = _appService.RemoteIpAddress,
|
||||
Device = App.GetService<IAppService>().ClientInfo?.OS?.ToString(),
|
||||
Device = _appService.UserAgent?.Platform,
|
||||
Expire = expire,
|
||||
SysUser = sysUser,
|
||||
VerificatId = verificatId
|
||||
};
|
||||
await WriteTokenToCache(loginPolicy, logingEvent).ConfigureAwait(false);//写入verificat到cache
|
||||
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
|
||||
{
|
||||
VerificatId = verificatId,
|
||||
Account = sysUser.Account,
|
||||
Id = sysUser.Id,
|
||||
ModuleList = sysUser.ModuleList,
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken
|
||||
};
|
||||
|
@@ -77,7 +77,7 @@ internal sealed class SysDictService : BaseService<SysDict>, ISysDictService
|
||||
//更新数据
|
||||
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);
|
||||
|
||||
|
@@ -16,9 +16,9 @@ namespace ThingsGateway.Admin.Application;
|
||||
/// 内存推送事件服务
|
||||
/// </summary>
|
||||
/// <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()
|
||||
{
|
||||
|
@@ -11,8 +11,6 @@
|
||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +26,7 @@ public class UserIdProvider : IUserIdProvider
|
||||
|
||||
if (UserId > 0)
|
||||
{
|
||||
return $"{UserId}{SysHub.Separate}{YitIdHelper.NextId()}";//返回用户ID
|
||||
return $"{UserId}{SysHub.Separate}{CommonUtils.GetSingleId()}";//返回用户ID
|
||||
}
|
||||
|
||||
return connection.ConnectionId;
|
||||
|
@@ -10,10 +10,10 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Data;
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
internal sealed class SysOperateLogService : BaseService<SysOperateLog>, ISysOperateLogService
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -10,10 +10,9 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.FriendlyException;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -10,9 +10,8 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.FriendlyException;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -12,11 +12,10 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
using ThingsGateway.FriendlyException;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -334,7 +333,7 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
|
||||
flatList.Add(node);
|
||||
|
||||
// 如果当前节点有子节点,则递归处理每个子节点
|
||||
if (node.Children != null && node.Children.Count > 0)
|
||||
if (node.Children?.Count > 0)
|
||||
{
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -10,10 +10,9 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.FriendlyException;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -277,7 +276,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
||||
if (isSuperAdmin)
|
||||
throw Oops.Bah(Localizer["CanotGrantAdmin"]);
|
||||
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 sysRole = (await GetAllAsync().ConfigureAwait(false)).FirstOrDefault(it => it.Id == input.Id);//获取角色
|
||||
|
||||
@@ -338,7 +337,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
||||
ExtJson = new RelationPermission
|
||||
{
|
||||
ApiUrl = it.ApiRoute,
|
||||
}.ToJsonNetString()
|
||||
}.ToSystemTextJsonString()
|
||||
});
|
||||
relationRoles.AddRange(relationRolePer);//合并列表
|
||||
}
|
||||
@@ -410,7 +409,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
||||
if (sysRole != null)
|
||||
{
|
||||
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);//添加到数据库
|
||||
await ClearTokenUtil.DeleteUserCacheByRoleIds(new List<long> { input.Id }).ConfigureAwait(false);//清除角色下用户缓存
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using SqlSugar;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
@@ -12,13 +12,12 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.DataEncryption;
|
||||
using ThingsGateway.Extension;
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.FriendlyException;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -435,7 +434,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
if (sysUser != null)
|
||||
{
|
||||
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);//添加到数据库
|
||||
DeleteUserFromCache(input.Id);
|
||||
}
|
||||
@@ -466,7 +465,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
var exist = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户信息
|
||||
if (exist != null)
|
||||
{
|
||||
var isSuperAdmin = exist.Account == RoleConst.SuperAdmin;//判断是否有超管
|
||||
var isSuperAdmin = exist.Id == RoleConst.SuperAdminId;//判断是否有超管
|
||||
if (isSuperAdmin && !UserManager.SuperAdmin)
|
||||
throw Oops.Bah(Localizer["CanotEditAdminUser"]);
|
||||
|
||||
@@ -540,7 +539,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||||
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)
|
||||
throw Oops.Bah(Localizer["CanotGrantAdmin"]);
|
||||
|
||||
@@ -557,7 +556,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
public async Task GrantResourceAsync(GrantResourceData input)
|
||||
{
|
||||
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 sysUser = await GetUserByIdAsync(input.Id).ConfigureAwait(false);//获取用户
|
||||
await CheckApiDataScopeAsync(sysUser.OrgId, sysUser.CreateUserId).ConfigureAwait(false);
|
||||
@@ -613,7 +612,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
TargetId = it.ApiRoute,
|
||||
Category = RelationCategoryEnum.UserHasPermission,
|
||||
ExtJson = new RelationPermission { ApiUrl = it.ApiRoute }
|
||||
.ToJsonNetString()
|
||||
.ToSystemTextJsonString()
|
||||
});
|
||||
relationUsers.AddRange(relationUserPer);//合并列表
|
||||
}
|
||||
@@ -660,7 +659,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
public async Task<bool> DeleteUserAsync(IEnumerable<long> ids)
|
||||
{
|
||||
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)
|
||||
throw Oops.Bah(Localizer["CanotDeleteAdminUser"]);
|
||||
if (ids.Contains(UserManager.UserId))
|
||||
@@ -899,7 +898,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
var tenantId = await _sysOrgService.GetTenantIdByOrgIdAsync(sysUser.OrgId, sysOrgList).ConfigureAwait(false);
|
||||
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);
|
||||
sysUser.ModuleList = modules.ToList();//模块列表赋值给用户
|
||||
|
@@ -203,7 +203,7 @@ internal sealed class UserCenterService : BaseService<SysUser>, IUserCenterServi
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -8,10 +8,7 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.List;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -40,6 +37,8 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
|
||||
|
||||
private VerificatInfo? GetFromDb(long id)
|
||||
{
|
||||
if (id == 0)
|
||||
return null;
|
||||
using var db = GetDB();
|
||||
var verificatInfo = db.Queryable<VerificatInfo>().First(u => u.Id == id);
|
||||
if (verificatInfo != null)
|
||||
@@ -169,7 +168,7 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
|
||||
public void RemoveAllClientId()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
@@ -13,8 +13,6 @@ using BootstrapBlazor.Components;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.UnifyResult;
|
||||
@@ -28,18 +26,12 @@ public class Startup : AppStartup
|
||||
{
|
||||
Directory.CreateDirectory("DB");
|
||||
|
||||
services.AddConfigurableOptions<SqlSugarOptions>();
|
||||
services.AddConfigurableOptions<AdminLogOptions>();
|
||||
services.AddConfigurableOptions<TenantOptions>();
|
||||
|
||||
services.AddSingleton(typeof(IDataService<>), typeof(BaseService<>));
|
||||
services.AddSingleton<ISugarAopService, SugarAopService>();
|
||||
services.AddSingleton<ISugarConfigAopService, SugarConfigAopService>();
|
||||
|
||||
services.AddSingleton<IUserAgentService, UserAgentService>();
|
||||
services.AddSingleton<IAppService, AppService>();
|
||||
|
||||
StaticConfig.EnableAllWhereIF = true;
|
||||
|
||||
services.AddConfigurableOptions<EmailOptions>();
|
||||
services.AddConfigurableOptions<HardwareInfoOptions>();
|
||||
|
||||
@@ -56,7 +48,6 @@ public class Startup : AppStartup
|
||||
|
||||
services.AddSingleton<IVerificatInfoService, VerificatInfoService>();
|
||||
services.AddSingleton<IUserCenterService, UserCenterService>();
|
||||
services.AddSingleton<ISugarAopService, SugarAopService>();
|
||||
services.AddSingleton<ISysDictService, SysDictService>();
|
||||
services.AddSingleton<ISysOperateLogService, SysOperateLogService>();
|
||||
services.AddSingleton<IRelationService, RelationService>();
|
||||
@@ -89,7 +80,7 @@ public class Startup : AppStartup
|
||||
DbContext.DbConfigs?.ForEach(it =>
|
||||
{
|
||||
var connection = DbContext.Db.GetConnection(it.ConfigId);//获取数据库连接对象
|
||||
if (it.InitTable == true)
|
||||
if (it.InitDatabase == true)
|
||||
connection.DbMaintenance.CreateDatabase();//创建数据库,如果存在则不创建
|
||||
});
|
||||
|
||||
@@ -97,6 +88,21 @@ public class Startup : AppStartup
|
||||
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>();
|
||||
verificatInfoService.RemoveAllClientId();
|
||||
|
@@ -18,10 +18,7 @@
|
||||
</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="SqlSugarCore" Version="5.1.4.191" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
||||
@@ -30,9 +27,9 @@
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
|
||||
<PackageReference Include="System.Formats.Asn1" Version="9.0.4" />
|
||||
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(NET9Version)" />
|
||||
<PackageReference Include="System.Formats.Asn1" Version="$(NET9Version)" />
|
||||
<PackageReference Include="System.Threading.RateLimiting" Version="$(NET9Version)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove="SeedData\Admin\*.json" />
|
||||
@@ -50,6 +47,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
|
||||
<ProjectReference Include="..\ThingsGateway.DB\ThingsGateway.DB.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</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;
|
||||
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class ClearTokenUtil
|
||||
public static class ClearTokenUtil
|
||||
{
|
||||
private static IRelationService RelationService;
|
||||
private static ISysUserService SysUserService;
|
||||
|
@@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class NoticeUtil
|
||||
public static class NoticeUtil
|
||||
{
|
||||
private static INoticeService NoticeService;
|
||||
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class OpenApiUtil
|
||||
public static class OpenApiUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建树节点,传入的列表已经是树结构
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class OrgUtil
|
||||
public static class OrgUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造选择项,ID/TITLE
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class PositionUtil
|
||||
public static class PositionUtil
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class RoleUtil
|
||||
public static class RoleUtil
|
||||
{
|
||||
|
||||
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class UserUtil
|
||||
public static class UserUtil
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
|
@@ -8,13 +8,12 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public class VerificatInfoUtil
|
||||
public static class VerificatInfoUtil
|
||||
{
|
||||
private static IVerificatInfoService VerificatInfoService { get; set; }
|
||||
static VerificatInfoUtil()
|
||||
|
@@ -11,7 +11,7 @@
|
||||
namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class AdminOperConst
|
||||
public static class AdminOperConst
|
||||
{
|
||||
public const string Add = "新增";
|
||||
public const string Delete = "删除";
|
||||
|
@@ -40,6 +40,8 @@ public class BlazorAppContext
|
||||
/// </summary>
|
||||
public SysUser CurrentUser { get; private set; }
|
||||
|
||||
public string? Avatar => UserManager.AvatarUrl.IsNullOrEmpty() ? CurrentUser.Avatar : UserManager.AvatarUrl;
|
||||
|
||||
/// <summary>
|
||||
/// 用户个人菜单
|
||||
/// </summary>
|
||||
@@ -97,7 +99,7 @@ public class BlazorAppContext
|
||||
AllResource = sysResources;
|
||||
var ids = CurrentUser.ModuleList.Select(a => a.Id).ToHashSet();
|
||||
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)
|
||||
{
|
||||
|
@@ -1,74 +1,73 @@
|
||||
{
|
||||
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
|
||||
"Jump": "Jump"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserLogin": {
|
||||
"Account": "Username",
|
||||
"Password": "Password",
|
||||
"Login": "Login"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor._Imports": {
|
||||
"Org": "Org",
|
||||
"Position": "Position",
|
||||
"Role": "Role",
|
||||
"RoleList": "Role List",
|
||||
"PositionList": "Position List",
|
||||
"OrgList": "Org List",
|
||||
"MaxCount": "Exceeding the quantity limit",
|
||||
"All": "All",
|
||||
"Copy": "Copy",
|
||||
"Choice": "Choice",
|
||||
"Picture": "Picture",
|
||||
"Root": "Root",
|
||||
"Save": "Save",
|
||||
"Copy": "Copy",
|
||||
"CurrentVerificat": "CurrentVerificat",
|
||||
"EmptyText": "Empty",
|
||||
"SelectPlaceHolder": "Please select ...",
|
||||
"ExportButtonText": "Export/Import"
|
||||
"ExportButtonText": "Export/Import",
|
||||
"MaxCount": "Exceeding the quantity limit",
|
||||
"Org": "Org",
|
||||
"OrgList": "Org List",
|
||||
"Picture": "Picture",
|
||||
"Position": "Position",
|
||||
"PositionList": "Position List",
|
||||
"Role": "Role",
|
||||
"RoleList": "Role List",
|
||||
"Root": "Root",
|
||||
"Save": "Save",
|
||||
"SelectPlaceHolder": "Please select ..."
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.ChoiceModuleComponent": {
|
||||
"SetDefaultModule": "Set as default module"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
||||
"SystemInfo": "System Information",
|
||||
"AvailableDisk": "Available Disk",
|
||||
"AvailableMemory": "Available Memory",
|
||||
"CpuUsage": "CPU Usage",
|
||||
"Data": "Data",
|
||||
"DateTime": "Date Time",
|
||||
"DiskUsage": "Disk Usage",
|
||||
"HardwareInfo": "Hardware Resources",
|
||||
"HardwareInfoChart": "Hardware Resources History Chart",
|
||||
"CpuUsage": "CPU Usage",
|
||||
"AvailableMemory": "Available Memory",
|
||||
"TotalMemory": "Total Memory",
|
||||
"WorkingSet": "WorkingSet",
|
||||
"MemoryUsage": "Memory",
|
||||
"AvailableDisk": "Available Disk",
|
||||
"TotalDisk": "Total Disk",
|
||||
"DiskUsage": "Disk Usage",
|
||||
"HistoryHardwareInfo": "Historical Trends",
|
||||
"DateTime": "Date Time",
|
||||
"Data": "Data",
|
||||
"MemoryUsage": "Memory",
|
||||
"OSName": "OS Name",
|
||||
"OSVersion": "OS Version"
|
||||
"OSVersion": "OS Version",
|
||||
"SystemInfo": "System Information",
|
||||
"TotalDisk": "Total Disk",
|
||||
"TotalMemory": "Total Memory",
|
||||
"WorkingSet": "WorkingSet"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
|
||||
"Jump": "Jump"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.OperLogPage": {
|
||||
"Date": "Date",
|
||||
"Count": "Count",
|
||||
"Operate": "Succeed",
|
||||
"Date": "Date",
|
||||
"Exception": "Failed",
|
||||
"Login": "Login",
|
||||
"Logout": "Logout",
|
||||
"Operate": "Succeed",
|
||||
"SysOperateLog": "Operation Log"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.SessionPage": {
|
||||
"VerificatInfo": "Token List",
|
||||
"ExitSession": "Force Logout"
|
||||
"ExitSession": "Force Logout",
|
||||
"VerificatInfo": "Token List"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserCenterPage": {
|
||||
"UpdatePasswordInfo": "Change Password",
|
||||
"UpdateUserInfo": "Update Personal Information",
|
||||
"UpdateWorkbenchInfo": "Update Personal Workbench",
|
||||
"UserInfo": "Personal Information",
|
||||
"WorkbenchInfo": "Personal Workbench"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserLogin": {
|
||||
"Account": "Username",
|
||||
"Login": "Login",
|
||||
"Password": "Password"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.VerificatListDialog": {
|
||||
"ExitVerificat": "Force Logout Token"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserCenterPage": {
|
||||
"UpdateUserInfo": "Update Personal Information",
|
||||
"UserInfo": "Personal Information",
|
||||
"UpdatePasswordInfo": "Change Password",
|
||||
"UpdateWorkbenchInfo": "Update Personal Workbench",
|
||||
"WorkbenchInfo": "Personal Workbench"
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,74 +1,73 @@
|
||||
{
|
||||
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
|
||||
"Jump": "跳转"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserLogin": {
|
||||
"Account": "登录账号",
|
||||
"Password": "登录密码",
|
||||
"Login": "登录"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor._Imports": {
|
||||
"All": "全部",
|
||||
"Choice": "选择",
|
||||
"Copy": "复制",
|
||||
"CurrentVerificat": "当前令牌",
|
||||
"EmptyText": "空",
|
||||
"ExportButtonText": "导出/导入",
|
||||
"MaxCount": "超过数量限制",
|
||||
"Org": "机构",
|
||||
"OrgList": "机构列表",
|
||||
"Picture": "头像",
|
||||
"Position": "岗位",
|
||||
"PositionList": "岗位列表",
|
||||
"Role": "角色",
|
||||
"RoleList": "角色列表",
|
||||
"PositionList": "岗位列表",
|
||||
"OrgList": "机构列表",
|
||||
"MaxCount": "超过数量限制",
|
||||
"All": "全部",
|
||||
"Copy": "复制",
|
||||
"Choice": "选择",
|
||||
"Picture": "头像",
|
||||
"Root": "根目录",
|
||||
"Save": "保存",
|
||||
"CurrentVerificat": "当前令牌",
|
||||
"SelectPlaceHolder": "请选择 ...",
|
||||
"EmptyText": "空",
|
||||
"ExportButtonText": "导出/导入"
|
||||
"SelectPlaceHolder": "请选择 ..."
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.ChoiceModuleComponent": {
|
||||
"SetDefaultModule": "设置为默认模块"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
|
||||
"SystemInfo": "系统信息",
|
||||
"AvailableDisk": "可用磁盘",
|
||||
"AvailableMemory": "可用内存",
|
||||
"CpuUsage": "CPU使用率",
|
||||
"Data": "数据",
|
||||
"DateTime": "时间",
|
||||
"DiskUsage": "磁盘使用率",
|
||||
"HardwareInfo": "硬件资源",
|
||||
"HardwareInfoChart": "硬件资源历史曲线",
|
||||
"CpuUsage": "CPU使用率",
|
||||
"AvailableMemory": "可用内存",
|
||||
"TotalMemory": "总内存",
|
||||
"WorkingSet": "进程内存",
|
||||
"MemoryUsage": "内存",
|
||||
"AvailableDisk": "可用磁盘",
|
||||
"TotalDisk": "总磁盘",
|
||||
"DiskUsage": "磁盘使用率",
|
||||
"HistoryHardwareInfo": "历史曲线",
|
||||
"DateTime": "时间",
|
||||
"Data": "数据",
|
||||
"MemoryUsage": "内存",
|
||||
"OSName": "系统名称",
|
||||
"OSVersion": "系统版本"
|
||||
"OSVersion": "系统版本",
|
||||
"SystemInfo": "系统信息",
|
||||
"TotalDisk": "总磁盘",
|
||||
"TotalMemory": "总内存",
|
||||
"WorkingSet": "进程内存"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
|
||||
"Jump": "跳转"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.OperLogPage": {
|
||||
"Date": "日期",
|
||||
"Count": "数量",
|
||||
"Operate": "成功",
|
||||
"Date": "日期",
|
||||
"Exception": "失败",
|
||||
"Login": "登录",
|
||||
"Logout": "注销",
|
||||
"Operate": "成功",
|
||||
"SysOperateLog": "操作日志"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.SessionPage": {
|
||||
"VerificatInfo": "令牌列表",
|
||||
"ExitSession": "强退会话"
|
||||
"ExitSession": "强退会话",
|
||||
"VerificatInfo": "令牌列表"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserCenterPage": {
|
||||
"UpdatePasswordInfo": "修改密码",
|
||||
"UpdateUserInfo": "更新个人信息",
|
||||
"UpdateWorkbenchInfo": "更新个人工作台",
|
||||
"UserInfo": "个人信息",
|
||||
"WorkbenchInfo": "个人工作台"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserLogin": {
|
||||
"Account": "登录账号",
|
||||
"Login": "登录",
|
||||
"Password": "登录密码"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.VerificatListDialog": {
|
||||
"ExitVerificat": "强退令牌"
|
||||
},
|
||||
"ThingsGateway.Admin.Razor.UserCenterPage": {
|
||||
"UpdateUserInfo": "更新个人信息",
|
||||
"UserInfo": "个人信息",
|
||||
"UpdatePasswordInfo": "修改密码",
|
||||
"UpdateWorkbenchInfo": "更新个人工作台",
|
||||
"WorkbenchInfo": "个人工作台"
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,10 +8,9 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
|
@@ -8,9 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
|
@@ -8,10 +8,9 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
|
@@ -8,10 +8,9 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
|
@@ -41,7 +41,7 @@ public partial class SessionPage
|
||||
{
|
||||
var op = new DialogOption()
|
||||
{
|
||||
IsScrolling = false,
|
||||
IsScrolling = true,
|
||||
Title = Localizer[nameof(VerificatInfo)],
|
||||
ShowMaximizeButton = true,
|
||||
Class = "dialog-table",
|
||||
|
@@ -1,7 +0,0 @@
|
||||
@namespace ThingsGateway.Admin.Razor
|
||||
@using ThingsGateway.Admin.Application
|
||||
|
||||
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsSingle Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>
|
||||
|
||||
|
||||
|
@@ -1,73 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
|
||||
namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
public partial class SysUserAvatarEdit : IDisposable
|
||||
{
|
||||
private List<UploadFile> PreviewFileList;
|
||||
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public SysUser? Model { get; set; }
|
||||
|
||||
[FileValidation(Extensions = [".png", ".jpg", ".jpeg"], FileSize = 200 * 1024)]
|
||||
public IBrowserFile? Picture { get; set; }
|
||||
|
||||
[Inject]
|
||||
private IStringLocalizer<ThingsGateway.Admin.Razor._Imports> AdminLocalizer { get; set; }
|
||||
|
||||
private CancellationTokenSource? ReadAvatarToken { get; set; }
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private ToastService ToastService { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReadAvatarToken?.Cancel();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected override Task OnParametersSetAsync()
|
||||
{
|
||||
PreviewFileList = new(new[] { new UploadFile { PrevUrl = Model.Avatar } });
|
||||
return base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
private async Task OnAvatarUpload(UploadFile file)
|
||||
{
|
||||
if (file != null && file.File != null)
|
||||
{
|
||||
var format = file.File.ContentType;
|
||||
ReadAvatarToken ??= new CancellationTokenSource();
|
||||
if (ReadAvatarToken.IsCancellationRequested)
|
||||
{
|
||||
ReadAvatarToken.Dispose();
|
||||
ReadAvatarToken = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, ReadAvatarToken.Token);
|
||||
|
||||
if (file.Code != 0)
|
||||
{
|
||||
await ToastService.Error($"{file.Error} ");
|
||||
}
|
||||
else
|
||||
{
|
||||
Model.Avatar = file.PrevUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@
|
||||
<div class="row g-2 mx-1 form-inline">
|
||||
|
||||
<div class="col-12 col-md-12">
|
||||
<SysUserAvatarEdit Model="Model"></SysUserAvatarEdit>
|
||||
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsMultiple=false Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
|
@@ -8,6 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
@@ -45,4 +47,55 @@ public partial class SysUserEdit
|
||||
Model.OrgId = items.LastOrDefault()?.Parent?.Value?.ToLong() ?? 0;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
[Inject]
|
||||
ToastService ToastService { get; set; }
|
||||
|
||||
#region 头像
|
||||
|
||||
private List<UploadFile> PreviewFileList;
|
||||
|
||||
[FileValidation(Extensions = [".png", ".jpg", ".jpeg"], FileSize = 200 * 1024)]
|
||||
public IBrowserFile? Picture { get; set; }
|
||||
|
||||
|
||||
private CancellationTokenSource? ReadAvatarToken { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReadAvatarToken?.Cancel();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
PreviewFileList = new(new[] { new UploadFile { PrevUrl = Model.Avatar } });
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private async Task OnAvatarUpload(UploadFile file)
|
||||
{
|
||||
if (file?.File != null)
|
||||
{
|
||||
var format = file.File.ContentType;
|
||||
ReadAvatarToken ??= new CancellationTokenSource();
|
||||
if (ReadAvatarToken.IsCancellationRequested)
|
||||
{
|
||||
ReadAvatarToken.Dispose();
|
||||
ReadAvatarToken = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, token: ReadAvatarToken.Token);
|
||||
|
||||
if (file.Code != 0)
|
||||
{
|
||||
await ToastService.Error($"{file.Error} ");
|
||||
}
|
||||
else
|
||||
{
|
||||
Model.Avatar = file.PrevUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 头像
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ public partial class UserCenterPage
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
SysUser = AppContext.CurrentUser.Adapt<SysUser>();
|
||||
SysUser.Avatar = AppContext.CurrentUser.Avatar;
|
||||
SysUser.Avatar = AppContext.Avatar;
|
||||
WorkbenchInfo = (await UserCenterService.GetLoginWorkbenchAsync(SysUser.Id)).Adapt<WorkbenchInfo>();
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<ValidateForm class="p-4" Model="@Model" OnValidSubmit="OnSave">
|
||||
|
||||
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsSingle Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>
|
||||
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsMultiple=false Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>
|
||||
|
||||
<BootstrapInput @bind-Value=@Model.Phone></BootstrapInput>
|
||||
<BootstrapInput @bind-Value=@Model.Email></BootstrapInput>
|
||||
|
@@ -40,15 +40,15 @@ public partial class UserInfoEditComponent
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected override Task OnParametersSetAsync()
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
PreviewFileList = new(new[] { new UploadFile { PrevUrl = Model.Avatar } });
|
||||
return base.OnParametersSetAsync();
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private async Task OnAvatarUpload(UploadFile file)
|
||||
{
|
||||
if (file != null && file.File != null)
|
||||
if (file?.File != null)
|
||||
{
|
||||
var format = file.File.ContentType;
|
||||
ReadAvatarToken ??= new CancellationTokenSource();
|
||||
@@ -58,7 +58,7 @@ public partial class UserInfoEditComponent
|
||||
ReadAvatarToken = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, ReadAvatarToken.Token);
|
||||
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, token: ReadAvatarToken.Token);
|
||||
|
||||
if (file.Code != 0)
|
||||
{
|
||||
|
@@ -9,10 +9,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.14" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET8Version)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET9Version)" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@@ -16,7 +16,7 @@ namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public class ResourceUtil
|
||||
public static class ResourceUtil
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
// nuget动态加载的程序集
|
||||
"SupportPackageNamePrefixs": [
|
||||
"ThingsGateway.SqlSugar",
|
||||
"ThingsGateway.Admin.Application",
|
||||
"ThingsGateway.Admin.Razor",
|
||||
"ThingsGateway.Razor"
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
// nuget动态加载的程序集
|
||||
"SupportPackageNamePrefixs": [
|
||||
"ThingsGateway.SqlSugar",
|
||||
"ThingsGateway.Admin.Application",
|
||||
"ThingsGateway.Admin.Razor",
|
||||
"ThingsGateway.Razor"
|
||||
|
1
src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs
Normal file
1
src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs
Normal file
@@ -0,0 +1 @@
|
||||
|
@@ -39,19 +39,4 @@
|
||||
</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,8 +18,6 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Razor;
|
||||
using ThingsGateway.Extension;
|
||||
@@ -31,118 +29,8 @@ namespace ThingsGateway.AdminServer;
|
||||
[IgnoreRolePermission]
|
||||
[Route("/")]
|
||||
[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]
|
||||
private BlazorAppContext AppContext { get; set; }
|
||||
|
@@ -25,6 +25,7 @@ using ThingsGateway.DataEncryption;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.Razor;
|
||||
|
||||
|
||||
namespace ThingsGateway.AdminServer;
|
||||
|
||||
public partial class Login
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user