Compare commits

...

41 Commits

Author SHA1 Message Date
Diego
57a4038577 pwa安装提示优化 2025-06-12 10:20:52 +08:00
Diego
b78a76e60f 增加采集设备写入数据的控制台文本日志 2025-06-11 23:49:37 +08:00
Diego
28bd751d44 增加tab右键菜单 2025-06-11 22:57:00 +08:00
Diego
dcba7b9810 10.7.50 2025-06-11 21:50:46 +08:00
Diego
7f1a741ce6 优化sql实时表执行性能 2025-06-11 21:39:14 +08:00
Diego
ca2b17d433 10.7.47 2025-06-11 17:08:19 +08:00
Diego
af589eac10 更新依赖 2025-06-11 15:05:03 +08:00
Diego
573670f1f5 10.7.45 2025-06-11 10:26:48 +08:00
Diego
f3ec85a03d 更新测试 2025-06-10 16:26:14 +08:00
Diego
c94308454f build: 10.7.42
feat:更改后台服务多语言处理方式
2025-06-10 15:49:47 +08:00
Diego
c9c2b2b69d 10.7.39 2025-06-09 16:11:37 +08:00
Diego
40574b776f feat(TDengine): 支持websocket连接,无需安装sdk 2025-06-09 12:38:30 +08:00
Diego
f426c1533d fix: taos插件 not found dll错误 2025-06-09 10:07:58 +08:00
Diego
4a3b5163f1 更新依赖 2025-06-09 09:29:41 +08:00
2248356998 qq.com
3a0946d357 优化单例db对象获取 2025-06-08 21:09:07 +08:00
Diego
9dd21c644c 10.7.35 2025-06-08 15:48:13 +08:00
2248356998 qq.com
6713190a23 日志写入器释放 2025-06-08 15:47:25 +08:00
Diego
9f24533425 10.7.34 2025-06-08 15:38:41 +08:00
2248356998 qq.com
ed1d9374eb 2025-06-08 01:22:41 +08:00
Diego
f14d27129e 10.7.33 2025-06-07 22:30:27 +08:00
2248356998 qq.com
5b04f02fbe Merge branch 'v10' of https://gitee.com/ThingsGateway/ThingsGateway into v10 2025-06-07 22:26:32 +08:00
2248356998 qq.com
227080e332 ddp协议清理旧连接逻辑错误 2025-06-07 22:26:18 +08:00
Diego
1e87482a49 10.7.32 2025-06-07 15:25:38 +08:00
2248356998 qq.com
054d31c3ea 添加自定义插件 2025-06-07 15:14:54 +08:00
Diego
3a068a7b03 更新解决方案 2025-06-07 12:22:05 +08:00
Diego
b12e923c99 10.7.31 2025-06-06 23:47:07 +08:00
Diego
ab33eed8d3 10.7.22 2025-06-06 21:13:58 +08:00
Diego
d930a9a8eb 修改演示站登录ui 2025-06-05 08:40:25 +08:00
Diego
af381fce12 插件调试增加双击显示 2025-06-04 09:45:02 +08:00
Diego
b64ac0539e 更新依赖 2025-06-03 22:27:28 +08:00
Diego
541c60b363 10.7.17 2025-06-02 19:45:56 +08:00
Diego
824e95f7cb 10.7.16 2025-06-02 19:44:30 +08:00
youthalan
38f7850196 !66 修复缺少引用编译错误
* 修复缺少引用编译错误
2025-05-31 06:35:47 +00:00
Diego
bef9de88e2 oauth增加scope参数 2025-05-31 02:17:09 +08:00
Diego
48cd5e7c7f 增加演示站的gitee授权 2025-05-31 00:43:43 +08:00
Diego
3b44fda51c 修复hybrid程序 2025-05-30 13:48:03 +08:00
Diego
dbfc9a5bb4 更新授权显示 2025-05-30 11:21:45 +08:00
Diego
1b758aa41a 10.6.37 2025-05-30 10:15:58 +08:00
Diego
43bdc70899 优化文本日志读取 2025-05-30 10:02:51 +08:00
Diego
fadda000a6 添加sqlsugar类 2025-05-29 22:18:33 +08:00
Diego
45a8c91a5a 优化UI刷新 2025-05-29 17:05:06 +08:00
1101 changed files with 116483 additions and 5914 deletions

3
.gitignore vendored
View File

@@ -365,4 +365,5 @@ FodyWeavers.xsd
/src/*Pro*/
/src/*Pro*
/src/*pro*
/src/*pro*/
/src/*pro*/
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json

View File

@@ -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

View File

@@ -64,7 +64,7 @@ public sealed class OperDescAttribute : MoAttribute
public override void OnException(MethodContext context)
{
if (App.HttpContext.Request.Path.StartsWithSegments("/_blazor"))
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
{
//插入异常日志
SysOperateLog log = GetOperLog(LocalizerType, context);
@@ -82,7 +82,7 @@ public sealed class OperDescAttribute : MoAttribute
public override void OnSuccess(MethodContext context)
{
if (App.HttpContext.Request.Path.StartsWithSegments("/_blazor"))
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
{
//插入操作日志

View File

@@ -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
{
}

View File

@@ -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
{
}

View File

@@ -11,7 +11,7 @@
namespace ThingsGateway.Admin.Application;
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class CacheConst
public static class CacheConst
{
/// <summary>
/// Token表缓存Key

View File

@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
/// <summary>
/// 通讯器常量
/// </summary>
public class HubConst
public static class HubConst
{
/// <summary>
/// 系统HubUrl

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// 资源表常量
/// </summary>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class ResourceConst
public static class ResourceConst
{
/// <summary>
/// 系统内置编码

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// 角色常量
/// </summary>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class RoleConst
public static class RoleConst
{
/// <summary>
/// api角色

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// SqlSugar系统常量
/// </summary>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class SqlSugarConst
public static class SqlSugarConst
{
/// <summary>
/// DB_Admin

View File

@@ -8,6 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -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();

View File

@@ -10,10 +10,10 @@
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
[SugarTable("sys_dict", TableDescription = "字典表")]

View File

@@ -10,7 +10,7 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,10 +10,10 @@
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -10,10 +10,10 @@
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,7 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -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>

View File

@@ -10,10 +10,10 @@
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -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>

View File

@@ -10,9 +10,8 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.List;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -1,14 +1,4 @@
// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站https://baiqian.com
//
// 许可证信息
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------

using ThingsGateway.DependencyInjection;
namespace System;

View File

@@ -8,6 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using System.Reflection;
namespace ThingsGateway.Admin.Application;
public class RequestAuditData
@@ -94,5 +96,6 @@ public class RequestAuditData
/// 验证错误信息
/// </summary>
public Validation Validation { get; set; }
public MethodInfo MethodInfo { get; set; }
}

View File

@@ -123,12 +123,12 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
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;

View File

@@ -1,14 +1,4 @@
// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站https://baiqian.com
//
// 许可证信息
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------

using ThingsGateway.DependencyInjection;
namespace System;

View File

@@ -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)
{
}
}

View File

@@ -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; } = "/";
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,7 @@
namespace ThingsGateway.Admin.Application;
public class GiteeOAuthSettings
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -110,7 +110,7 @@ public class HardwareJob : IJob, IHardwareJob
catch (Exception ex)
{
if (!error)
_logger.LogWarning(ex, _localizer["GetHardwareInfoFail"]);
_logger.LogWarning(ex, "Get Hardwareinfo Fail");
error = true;
}
@@ -152,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;
}
}

View File

@@ -8,7 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -1,459 +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": {
"SortCode": "SortCode",
"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",
"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",
"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",
"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",
"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",
"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",
"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"
}
}
}

View File

@@ -1,462 +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": {
"SortCode": "排序",
"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": "最新登录地点",
"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": "分类",
"Global": "全局",
"Status": "状态",
"OrgId": "机构",
"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": "自定义"
"SCOPE_ORG_DEFINE": "自定义",
"SCOPE_SELF": "仅自己"
},
"ThingsGateway.Admin.Application.DefaultDataScope": {
"ScopeCategory": "数据范围",
"ScopeDefineOrgIdList": "自定义列表"
},
"ThingsGateway.Admin.Application.SysResource": {
"Title": "标题",
"Module": "模块",
"Title.Required": "{0} 是必填项",
"Href.Required": "{0} 是必填项",
"Icon": "图标",
"Href": "路径",
"Code": "编码",
"Category": "分类",
"Target": "跳转类型",
"NavLinkMatch": "匹配类型",
"ParentId": "上级菜单",
"ResourceDup": "存在重复的名称 {0}",
"ResourceParentChoiceSelf": "父级不能选择自己",
"ResourceParentNull": "父级不存在 {0}",
"NotFoundResource": "系统异常,没找到该菜单",
"ModuleIdDiff": "模块与上级菜单不一致",
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
"ResourceMenuHrefNotNull": "菜单的路径不能为空"
"ThingsGateway.Admin.Application.DictTypeEnum": {
"Define": "业务配置",
"System": "系统配置"
},
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"TargetId": "目标机构",
"ContainsChild": "包含下级",
"ContainsPosition": "包含职位"
"ThingsGateway.Admin.Application.FileService": {
"FileLengthError": "文件大小不允许超过 {0} M",
"FileNullError": "文件不能为空",
"FileTypeError": "不支持 {0} 格式"
},
"ThingsGateway.Admin.Application.SysPosition": {
"Category.Required": "{0} 是必填项",
"Name.Required": "{0} 是必填项",
"Code.Required": "{0} 是必填项",
"OrgId.MinValue": "{0} 是必填项",
"Category": "分类",
"Name": "名称",
"Code": "代码",
"Status": "状态",
"OrgId": "机构",
"Remark": "备注",
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
"CodeDup": "存在重复的编码 {0}",
"NameDup": "存在重复的名称 {0}",
"CanotContainsSelf": "不可包含自己",
"TargetNameDup": "目标节点存在重复的名称 {0}",
"ParentChoiceSelf": "父级不能选择自己",
"ParentNull": "父级不存在 {0}",
"DeleteUserFirst": "请先删除职位下的用户"
"ThingsGateway.Admin.Application.HardwareInfo": {
"Environment": "主机环境",
"FrameworkDescription": "NET框架",
"OsArchitecture": "系统架构",
"UpdateTime": "更新时间",
"UUID": "唯一编码"
},
"ThingsGateway.Admin.Application.SysOrg": {
"Category.Required": "{0} 是必填项",
"Name.Required": "{0} 是必填项",
"Code.Required": "{0} 是必填项",
"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": "分类",
"Name": "名称",
"Code": "代码",
"Status": "状态",
"ParentId": "上级机构",
"Names": "机构全称",
"Remark": "备注",
"DirectorId": "主管",
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
"CodeDup": "存在重复的编码 {0}",
"NameDup": "存在重复的名称 {0}",
"CanotContainsSelf": "不可包含自己",
"TargetNameDup": "目标节点存在重复的名称 {0}",
"ParentChoiceSelf": "父级不能选择自己",
"ParentNull": "父级不存在 {0}",
"DeleteUserFirst": "请先删除机构下的用户",
"DeleteRoleFirst": "请先删除机构下的角色",
"DeletePositionFirst": "请先删除机构下的职位",
"RootOrg": "无法创建顶层机构"
"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": "高层",
"MIDDLE": "层",
"LOW": "层"
"LOW": "层",
"MIDDLE": "层"
},
//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.ResourceCategoryEnum": {
"Button": "按钮",
"Menu": "菜单",
"Module": "模块"
},
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
"Module": "模块",
"Href": "路径",
"Module": "模块",
"Title": "标题"
},
"ThingsGateway.Admin.Application.WorkbenchInfo": {
"Razor": "主页",
"Shortcuts": "快捷方式"
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
"Global": "全局",
"Org": "机构"
},
"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": "最新登录时间",
"Online": "在线状态",
"VerificatCount": "令牌数量"
},
"ThingsGateway.Admin.Application.SysDict": {
"Category.Required": "{0} 是必填项",
"Name.Required": "{0} 是必填项",
"Code.Required": "{0} 是必填项",
"Category": "分类",
"Name": "名称",
"Category.Required": "{0} 是必填项",
"Code": "代码",
"Remark": "备注",
"Code.Required": "{0} 是必填项",
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置",
"DictDup": "存在重复的配置 分类 {0} 名称 {1}",
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置"
"Name": "名称",
"Name.Required": "{0} 是必填项",
"Remark": "备注"
},
"ThingsGateway.Admin.Application.SysOperateLog": {
"Category": "日志分类",
"ClassName": "类名",
"ExeMessage": "具体消息",
"MethodName": "方法名称",
"ParamJson": "请求参数",
"ReqMethod": "请求方式",
"ReqUrl": "请求地址",
"ResultJson": "返回结果",
"Category": "日志分类",
"ExeStatus": "执行状态",
"MethodName": "方法名称",
"Name": "日志名称",
"OpAccount": "账号",
"OpBrowser": "浏览器",
"OpIp": "ip",
"OpOs": "系统",
"OpTime": "操作时间",
"ParamJson": "请求参数",
"ReqMethod": "请求方式",
"ReqUrl": "请求地址",
"ResultJson": "返回结果",
"VerificatId": "验证Id"
},
"ThingsGateway.Admin.Application.OperateLogPageInput": {
"SearchDate": "时间范围",
"Account": "操作账号",
"Category": "分类"
"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.LoginInput": {
"Account": "登录账号",
"Password": "登录密码",
"Account.Required": "{0} 是必填项",
"Password.Required": "{0} 是必填项"
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"ContainsChild": "包含下级",
"ContainsPosition": "包含职位",
"TargetId": "目标机构"
},
"ThingsGateway.Admin.Application.LogoutInput": {
"VerificatId.Required": "{0} 是必填项"
"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.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": {
"ThingsGateway.Admin.Application.SysResource": {
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
"Category": "分类",
"Code": "编码",
"Href": "路径",
"Href.Required": "{0} 是必填项",
"Icon": "图标",
"Module": "模块",
"Menu": "菜单",
"Button": "按钮"
"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": {
"_self": "本窗口",
"_blank": "新窗口",
"_parent": "父级窗口",
"_self": "本窗口",
"_top": "顶级窗口"
},
"ThingsGateway.Admin.Application.DictTypeEnum": {
"System": "系统配置",
"Define": "业务配置"
"ThingsGateway.Admin.Application.TestController": {
"Test": "测试",
"TestController": "测试API"
},
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
"Login": "登录",
"Logout": "注销",
"Operate": "操作",
"Exception": "异常"
"ThingsGateway.Admin.Application.UnifyResultProvider": {
"NoPermission": "禁止访问,没有权限",
"TokenOver": "登录已过期,请重新登录"
},
"ThingsGateway.Admin.Application.LogEnum": {
"SUCCESS": "成功",
"FAIL": "失败"
"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": "快捷方式"
}
}
}

View File

@@ -8,14 +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 ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
@@ -51,13 +52,18 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
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 (requestAuditData.Exception == null)
{
save = await CreateVisitLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false);//添加到访问日志
LogCateGoryEnum logCateGoryEnum = login != null ? LogCateGoryEnum.Login : LogCateGoryEnum.Logout;
save = await CreateVisitLog(operation, path, requestAuditData, client, logCateGoryEnum, flush).ConfigureAwait(false);//添加到访问日志
}
else
{
@@ -151,17 +157,20 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
/// <param name="path"></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, RequestAuditData requestAuditData, UserAgent userAgent, 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)
{
//如果是登录,用户信息就从返回值里拿
dynamic userInfo = requestAuditData.ReturnInformation;
opAccount = userInfo.Data.Account;//赋值账号
verificatId = userInfo.Data.VerificatId;
if (requestAuditData.ReturnInformation is UnifyResult<LoginOutput> userInfo)
{
opAccount = userInfo.Data.Account;//赋值账号
verificatId = userInfo.Data.VerificatId;
}
}
else
{
@@ -173,7 +182,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
var sysLogVisit = new SysOperateLog
{
Name = operation,
Category = path == "/api/auth/login" ? LogCateGoryEnum.Login : LogCateGoryEnum.Logout,
Category = logCateGoryEnum,
ExeStatus = true,
OpIp = requestAuditData.RemoteIPv4,
OpBrowser = userAgent?.Browser,

View File

@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
/// <summary>
/// 日志常量
/// </summary>
public class LoggingConst
public static class LoggingConst
{
/// <summary>
/// 分类

View File

@@ -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;
}

View File

@@ -20,9 +20,11 @@ namespace ThingsGateway.Admin.Application;
public class AppService : IAppService
{
private readonly IUserAgentService UserAgentService;
public AppService(IUserAgentService userAgentService)
private readonly IClaimsPrincipalService ClaimsPrincipalService;
public AppService(IUserAgentService userAgentService, IClaimsPrincipalService claimsPrincipalService)
{
UserAgentService = userAgentService;
ClaimsPrincipalService = claimsPrincipalService;
}
public string GetReturnUrl(string returnUrl)
{
@@ -70,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();

View File

@@ -8,31 +8,17 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.Extensions.Localization;
using System.Security.Claims;
namespace ThingsGateway.Foundation;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 语言资源
/// </summary>
public class DefaultResource
public class HybridClaimsPrincipalService : IClaimsPrincipalService
{
private static IStringLocalizer localizer;
/// <summary>
/// Localizer
/// </summary>
public static IStringLocalizer Localizer
HybridAppService _hybridAppService;
public HybridClaimsPrincipalService(HybridAppService hybridAppService)
{
get
{
if (localizer == null)
{
localizer = LocalizerUtil.GetLocalizer.Invoke(typeof(DefaultResource));
}
return localizer;
}
_hybridAppService = hybridAppService;
}
public ClaimsPrincipal? User => _hybridAppService.User;
//使用频率高的多语言应初始化构建
}

View File

@@ -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登录不再需要解密
{
@@ -237,25 +239,20 @@ public class AuthService : IAuthService
var logingEvent = new LoginEvent
{
Ip = _appService.RemoteIpAddress,
Device = App.GetService<IAppService>().UserAgent?.Platform,
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
};

View File

@@ -11,8 +11,6 @@
using Microsoft.AspNetCore.Http.Connections.Features;
using Microsoft.AspNetCore.SignalR;
using Yitter.IdGenerator;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -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

View File

@@ -10,7 +10,7 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,10 +10,9 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.Extension.Generic;
using ThingsGateway.FriendlyException;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,7 +10,7 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,9 +10,8 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.FriendlyException;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -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)
{

View File

@@ -10,7 +10,7 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,10 +10,9 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.FriendlyException;
using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -12,7 +12,7 @@ using BootstrapBlazor.Components;
using Mapster;
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -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;
@@ -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"]);
@@ -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();//模块列表赋值给用户

View File

@@ -8,7 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
@@ -37,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)

View File

@@ -13,8 +13,6 @@ using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
using System.Reflection;
using ThingsGateway.UnifyResult;
@@ -28,19 +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>();
@@ -57,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>();

View File

@@ -18,9 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.5" />
<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.193" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
@@ -29,9 +27,9 @@
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.5" />
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.5" />
<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" />
@@ -49,6 +47,7 @@
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
<ProjectReference Include="..\ThingsGateway.DB\ThingsGateway.DB.csproj" />
</ItemGroup>
</Project>

View File

@@ -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;

View File

@@ -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;

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class OpenApiUtil
public static class OpenApiUtil
{
/// <summary>
/// 构建树节点,传入的列表已经是树结构

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class OrgUtil
public static class OrgUtil
{
/// <summary>
/// 构造选择项ID/TITLE

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class PositionUtil
public static class PositionUtil
{
/// <summary>

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class RoleUtil
public static class RoleUtil
{

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class UserUtil
public static class UserUtil
{
/// <summary>

View File

@@ -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()

View File

@@ -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 = "删除";

View File

@@ -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>

View File

@@ -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"
}
}
}

View File

@@ -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": "个人工作台"
}
}
}

View File

@@ -8,10 +8,9 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Razor;

View File

@@ -8,9 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Razor;

View File

@@ -8,10 +8,9 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Razor;

View File

@@ -8,10 +8,9 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Razor;

View File

@@ -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>

View File

@@ -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;
}
}
}
}

View File

@@ -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">

View File

@@ -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
}

View File

@@ -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();

View File

@@ -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>

View File

@@ -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)
{

View File

@@ -9,10 +9,10 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET8Version)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET9Version)" />
</ItemGroup>
<PropertyGroup>

View File

@@ -16,7 +16,7 @@ namespace ThingsGateway.Admin.Razor;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class ResourceUtil
public static class ResourceUtil
{
/// <summary>

View File

@@ -11,6 +11,7 @@
// nuget动态加载的程序集
"SupportPackageNamePrefixs": [
"ThingsGateway.SqlSugar",
"ThingsGateway.Admin.Application",
"ThingsGateway.Admin.Razor",
"ThingsGateway.Razor"

View File

@@ -11,6 +11,7 @@
// nuget动态加载的程序集
"SupportPackageNamePrefixs": [
"ThingsGateway.SqlSugar",
"ThingsGateway.Admin.Application",
"ThingsGateway.Admin.Razor",
"ThingsGateway.Razor"

View File

@@ -0,0 +1 @@


View File

@@ -25,6 +25,7 @@ using ThingsGateway.DataEncryption;
using ThingsGateway.NewLife.Extension;
using ThingsGateway.Razor;
namespace ThingsGateway.AdminServer;
public partial class Login

View File

@@ -48,7 +48,7 @@
<CultureChooser />
</div>
<Logout ImageUrl="@(AppContext.CurrentUser.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
<Logout ImageUrl="@(AppContext.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
<LinkTemplate>
<a href=@("/") class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["系统首页"]</a>

View File

@@ -27,38 +27,6 @@ public partial class MainLayout : IDisposable
{
[Inject]
IStringLocalizer<ThingsGateway.Razor._Imports> RazorLocalizer { get; set; }
private Task OnRefresh(ContextMenuItem item, object? context)
{
if (context is TabItem tabItem)
{
_tab.Refresh(tabItem);
}
return Task.CompletedTask;
}
private async Task OnClose(ContextMenuItem item, object? context)
{
if (context is TabItem tabItem)
{
await _tab.RemoveTab(tabItem);
}
}
private Task OnCloseOther(ContextMenuItem item, object? context)
{
if (context is TabItem tabItem)
{
_tab.ActiveTab(tabItem);
}
_tab.CloseOtherTabs();
return Task.CompletedTask;
}
private Task OnCloseAll(ContextMenuItem item, object? context)
{
_tab.CloseAllTabs();
return Task.CompletedTask;
}
#region

View File

@@ -1,81 +1,77 @@
{
"ThingsGateway.AdminServer.NotFound404": {
"404": "Sorry, the page you are looking for does not exist.",
"401": "Sorry, you do not have permission to access this page.",
"Home": "Back to Home",
"Login": "Login"
"ThingsGateway.AdminServer.AdminIndex": {
"Alarm": "Real-time Alarm",
"AlarmCount": "Alarm Count",
"BackendLog": "Gateway Backend Log",
"BusinessDevice": "Business Device",
"CollectDevice": "Collect Device",
"Data": "Data",
"DateTime": "Date Time",
"HardwareInfoChart": "Hardware Information Historical Chart",
"HistoryHardwareInfo": "Historical Chart",
"OffLine": "Offline",
"OnLine": "Online",
"OperLog": "Recent Operations",
"RpcLog": "Gateway RPC Log",
"Shortcuts": "Shortcuts",
"Variable": "Variable"
},
"ThingsGateway.AdminServer.Login": {
"LoginErrorh1": "Login Error",
"LoginSuccessh1": "Login Success",
"LoginSuccessc1": "Redirecting to the page",
"LoginErrorh2": "Login Failed",
"LoginErrorc2": "Please contact the administrator!",
"LoginErrorh1": "Login Error",
"LoginErrorh2": "Login Failed",
"LoginSuccessc1": "Redirecting to the page",
"LoginSuccessh1": "Login Success",
"Remark1": "Admin",
"Remark2": "",
"Remark3": "Permission Framework Based on BlazorServer",
"Welcome": "Welcome"
},
"ThingsGateway.AdminServer.MainLayout": {
"About": "About",
"FullScreenButton": "Full Screen",
"UserCenter": "User Center",
"ChoiceModule": "Switch Module",
"LoginErrorh1": "Login Error",
"LoginSuccessh1": "Login Success",
"LoginSuccessc1": "Redirecting to the page",
"LoginErrorh2": "Login Failed",
"LoginErrorc2": "Please contact the administrator!",
"Logout": "Logout",
"系统首页": "Home",
"权限管理": "Permission",
"用户管理": "User",
"角色管理": "Role",
"菜单管理": "Menu",
"机构管理": "Organization",
"职位管理": "Position",
"系统运维": "SystemOperation",
"系统配置": "System",
"字典管理": "Dictionary",
"操作日志": "Operation",
"会话管理": "Session",
"硬件信息": "HardwareInfo",
"网关管理": "GatewayManagement",
"插件管理": "PluginManagement",
"插件调试": "PluginDebugging",
"通道管理": "Channel",
"采集设备": "CollectionDevices",
"业务设备": "BusinessDevices",
"变量管理": "Variable",
"网关状态": "GatewayStatus",
"设备状态": "Device",
"实时数据": "RealTimeData",
"实时报警": "RealTimeAlarms",
"网关日志": "GatewayLogs",
"采集设备": "CollectionDevices",
"菜单管理": "Menu",
"操作日志": "Operation",
"插件调试": "PluginDebugging",
"插件管理": "PluginManagement",
"后台日志": "Backend",
"RPC日志": "RPC",
"会话管理": "Session",
"机构管理": "Organization",
"角色管理": "Role",
"权限管理": "Permission",
"设备状态": "Device",
"实时报警": "RealTimeAlarms",
"实时数据": "RealTimeData",
"通道管理": "Channel",
"网关管理": "GatewayManagement",
"网关日志": "GatewayLogs",
"网关状态": "GatewayStatus",
"物联网关": "Gateway",
"系统管理": "Admin"
"系统管理": "Admin",
"系统配置": "System",
"系统首页": "Home",
"系统运维": "SystemOperation",
"业务设备": "BusinessDevices",
"硬件信息": "HardwareInfo",
"用户管理": "User",
"职位管理": "Position",
"字典管理": "Dictionary",
"About": "About",
"ChoiceModule": "Switch Module",
"FullScreenButton": "Full Screen",
"LoginErrorc2": "Please contact the administrator!",
"LoginErrorh1": "Login Error",
"LoginErrorh2": "Login Failed",
"LoginSuccessc1": "Redirecting to the page",
"LoginSuccessh1": "Login Success",
"Logout": "Logout",
"RPC日志": "RPC",
"UserCenter": "User Center"
},
"ThingsGateway.AdminServer.AdminIndex": {
"CollectDevice": "Collect Device",
"BusinessDevice": "Business Device",
"Variable": "Variable",
"Alarm": "Real-time Alarm",
"AlarmCount": "Alarm Count",
"OnLine": "Online",
"OffLine": "Offline",
"Shortcuts": "Shortcuts",
"OperLog": "Recent Operations",
"BackendLog": "Gateway Backend Log",
"RpcLog": "Gateway RPC Log",
"HardwareInfoChart": "Hardware Information Historical Chart",
"DateTime": "Date Time",
"Data": "Data",
"HistoryHardwareInfo": "Historical Chart"
"ThingsGateway.AdminServer.NotFound404": {
"401": "Sorry, you do not have permission to access this page.",
"404": "Sorry, the page you are looking for does not exist.",
"Home": "Back to Home",
"Login": "Login"
}
}
}

View File

@@ -1,85 +1,77 @@
{
"ThingsGateway.AdminServer.NotFound404": {
"404": "抱歉,您访问的页面不存在。",
"401": "抱歉,您无权限访问该页面。",
"Home": "回到首页",
"Login": "重新登录"
"ThingsGateway.AdminServer.AdminIndex": {
"Alarm": "实时报警",
"AlarmCount": "报警数量",
"BackendLog": "网关后台日志",
"BusinessDevice": "业务设备",
"CollectDevice": "采集设备",
"Data": "数据",
"DateTime": "时间",
"HardwareInfoChart": "硬件信息历史曲线",
"HistoryHardwareInfo": "历史曲线",
"OffLine": "离线",
"OnLine": "在线",
"OperLog": "最近操作",
"RpcLog": "网关RPC日志",
"Shortcuts": "快捷方式",
"Variable": "变量"
},
"ThingsGateway.AdminServer.Login": {
"LoginErrorh1": "登录异常",
"LoginSuccessh1": "登录成功",
"LoginSuccessc1": "即将跳转页面",
"LoginErrorh2": "登录失败",
"LoginErrorc2": "请联系管理员!",
"LoginErrorh1": "登录异常",
"LoginErrorh2": "登录失败",
"LoginSuccessc1": "即将跳转页面",
"LoginSuccessh1": "登录成功",
"Remark1": "后台管理",
"Remark2": "",
"Remark3": "基于BlazorServer的权限框架",
"Welcome": "欢迎使用"
},
"ThingsGateway.AdminServer.MainLayout": {
"About": "关于",
"FullScreenButton": "全屏",
"UserCenter": "个人中心",
"ChoiceModule": "切换模块",
"LoginErrorh1": "登录异常",
"LoginSuccessh1": "登录成功",
"LoginSuccessc1": "即将跳转页面",
"LoginErrorh2": "登录失败",
"LoginErrorc2": "请联系管理员!",
"Logout": "注销",
"系统首页": "系统首页",
"权限管理": "权限管理",
"用户管理": "用户管理",
"角色管理": "角色管理",
"菜单管理": "菜单管理",
"机构管理": "机构管理",
"职位管理": "职位管理",
"系统运维": "系统运维",
"系统配置": "系统配置",
"字典管理": "字典管理",
"操作日志": "操作日志",
"会话管理": "会话管理",
"硬件信息": "硬件信息",
"网关管理": "网关管理",
"插件管理": "插件管理",
"插件调试": "插件调试",
"通道管理": "通道管理",
"采集设备": "采集设备",
"业务设备": "业务设备",
"变量管理": "变量管理",
"网关状态": "网关状态",
"设备状态": "设备状态",
"实时数据": "实时数据",
"实时报警": "实时报警",
"网关日志": "网关日志",
"采集设备": "采集设备",
"菜单管理": "菜单管理",
"操作日志": "操作日志",
"插件调试": "插件调试",
"插件管理": "插件管理",
"后台日志": "后台日志",
"RPC日志": "RPC日志",
"会话管理": "会话管理",
"机构管理": "机构管理",
"角色管理": "角色管理",
"权限管理": "权限管理",
"设备状态": "设备状态",
"实时报警": "实时报警",
"实时数据": "实时数据",
"通道管理": "通道管理",
"网关管理": "网关管理",
"网关日志": "网关日志",
"网关状态": "网关状态",
"物联网关": "物联网关",
"系统管理": "系统管理"
"系统管理": "系统管理",
"系统配置": "系统配置",
"系统首页": "系统首页",
"系统运维": "系统运维",
"业务设备": "业务设备",
"硬件信息": "硬件信息",
"用户管理": "用户管理",
"职位管理": "职位管理",
"字典管理": "字典管理",
"About": "关于",
"ChoiceModule": "切换模块",
"FullScreenButton": "全屏",
"LoginErrorc2": "请联系管理员!",
"LoginErrorh1": "登录异常",
"LoginErrorh2": "登录失败",
"LoginSuccessc1": "即将跳转页面",
"LoginSuccessh1": "登录成功",
"Logout": "注销",
"RPC日志": "RPC日志",
"UserCenter": "个人中心"
},
"ThingsGateway.AdminServer.AdminIndex": {
"CollectDevice": "采集设备",
"BusinessDevice": "业务设备",
"Variable": "变量",
"Alarm": "实时报警",
"AlarmCount": "报警数量",
"OnLine": "在线",
"OffLine": "离线",
"Shortcuts": "快捷方式",
"OperLog": "最近操作",
"BackendLog": "网关后台日志",
"RpcLog": "网关RPC日志",
"HardwareInfoChart": "硬件信息历史曲线",
"DateTime": "时间",
"Data": "数据",
"HistoryHardwareInfo": "历史曲线"
"ThingsGateway.AdminServer.NotFound404": {
"401": "抱歉,您无权限访问该页面。",
"404": "抱歉,您访问的页面不存在。",
"Home": "回到首页",
"Login": "重新登录"
}
}
}

View File

@@ -40,7 +40,8 @@ public class SingleFilePublish : ISingleFilePublish
"ThingsGateway.NewLife.X",
"ThingsGateway.Razor",
"ThingsGateway.Admin.Razor" ,
"ThingsGateway.Admin.Application"
"ThingsGateway.Admin.Application",
"ThingsGateway.SqlSugar",
];
}
}

View File

@@ -369,12 +369,6 @@ public class Startup : AppStartup
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
app.UseStaticFiles();
app.Use(async (context, next) =>
{
context.Response.Headers.Append("ThingsGateway", "ThingsGateway");
await next().ConfigureAwait(false);
});
// 特定文件类型(文件后缀)处理
var contentTypeProvider = GetFileExtensionContentTypeProvider();

View File

@@ -45,7 +45,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
@@ -54,8 +54,8 @@
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET9Version)" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET9Version)" />
</ItemGroup>
<ItemGroup>

View File

@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
/// 授权用户常量
/// </summary>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public class ClaimConst
public static class ClaimConst
{
/// <summary>
/// 账号
@@ -31,6 +31,11 @@ public class ClaimConst
/// </summary>
public const string UserId = "UserId";
/// <summary>
/// AvatarUrl
/// </summary>
public const string AvatarUrl = "AvatarUrl";
/// <summary>
/// 验证Id
/// </summary>

View File

@@ -10,10 +10,10 @@
using BootstrapBlazor.Components;
using SqlSugar;
using System.Diagnostics.CodeAnalysis;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -10,7 +10,7 @@
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,11 +8,11 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using System.Linq.Expressions;
using System.Reflection;
using ThingsGateway.SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
@@ -225,7 +225,7 @@ public static class SqlSugarExtensions
private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
{
if (basePageInput != null && basePageInput.SortField != null)
if (basePageInput?.SortField != null)
{
for (int i = 0; i < basePageInput.SortField.Count; i++)
{

View File

@@ -0,0 +1,11 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
global using ThingsGateway.NewLife.Extension;

View File

@@ -0,0 +1,20 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using System.Security.Claims;
namespace ThingsGateway.Admin.Application;
public class ClaimsPrincipalService : IClaimsPrincipalService
{
public ClaimsPrincipal? User => App.User;
}

Some files were not shown because too many files have changed in this diff Show More