Compare commits

..

3 Commits

Author SHA1 Message Date
Diego
d33d900592 增加github oauth登录 2025-06-15 00:17:25 +08:00
Diego
29365c4ef9 修改脚本测试方法 2025-06-14 11:56:31 +08:00
Diego
17a6189089 style: 更新SysResourcePage页面样式 2025-06-13 16:03:28 +08:00
36 changed files with 520 additions and 462 deletions

View File

@@ -1,40 +0,0 @@
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

@@ -1,12 +0,0 @@
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

@@ -1,22 +0,0 @@
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

@@ -1,18 +1,14 @@
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;
@@ -80,6 +76,7 @@ public class AdminOAuthHandler<TOptions>(
AuthenticationProperties properties,
OAuthTokenResponse tokens)
{
Backchannel.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokens.AccessToken);
properties.RedirectUri = Options.HomePath;
properties.IsPersistent = true;
var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false);
@@ -90,7 +87,7 @@ public class AdminOAuthHandler<TOptions>(
properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result);
expire = (int)(result / 60.0);
}
var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false);
var user = await Options.HandleUserInfoAsync(Context, tokens).ConfigureAwait(false);
var loginEvent = await GetLogin(expire).ConfigureAwait(false);
await UpdateUser(loginEvent).ConfigureAwait(false);
@@ -148,43 +145,8 @@ public class AdminOAuthHandler<TOptions>(
}
/// <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)
{

View File

@@ -0,0 +1,87 @@
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using System.Net.Http.Headers;
using System.Text;
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;
};
Backchannel = new HttpClient(new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator // 若测试用
});
Backchannel.DefaultRequestHeaders.UserAgent.Add(
new ProductInfoHeaderValue("ThingsGateway", "1.0"));
}
/// <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; } = "/";
/// <summary>处理用户信息方法</summary>
public virtual async Task<JsonElement> HandleUserInfoAsync(HttpContext context, 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(UserInformationEndpoint, new Dictionary<string, string>
{
{ "access_token", tokens.AccessToken }
});
}
/// <summary>生成错误信息方法</summary>
protected 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();
}
}

View File

@@ -3,16 +3,20 @@ using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Admin.Application;
public class GiteeOAuthOptions : AdminOAuthOptions
{
INoticeService _noticeService;
IVerificatInfoService _verificatInfoService;
public GiteeOAuthOptions() : base()
{
_noticeService = App.GetService<INoticeService>();
_verificatInfoService = App.GetService<IVerificatInfoService>();
this.SignInScheme = ClaimConst.Scheme;
this.AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
this.TokenEndpoint = "https://gitee.com/oauth/token";
@@ -29,11 +33,14 @@ public class GiteeOAuthOptions : AdminOAuthOptions
Events.OnRedirectToAuthorizationEndpoint = context =>
{
//context.RedirectUri = context.RedirectUri.Replace("http%3A%2F%2F", "https%3A%2F%2F"); // 强制替换
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
Events.OnRemoteFailure = context =>
{
XTrace.WriteException(context.Failure);
return Task.CompletedTask;
};
}
/// <summary>刷新 Token 方法</summary>
@@ -60,16 +67,7 @@ public class GiteeOAuthOptions : AdminOAuthOptions
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)
{
@@ -77,7 +75,7 @@ public class GiteeOAuthOptions : AdminOAuthOptions
return target.TryGetValue("name");
}
private static async Task HandlerGiteeStarredUrl(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
private async Task HandlerGiteeStarredUrl(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
{
if (string.IsNullOrWhiteSpace(context.AccessToken))
throw new InvalidOperationException("Access token is missing.");
@@ -89,7 +87,7 @@ public class GiteeOAuthOptions : AdminOAuthOptions
{ "access_token", context.AccessToken }
};
var request = new HttpRequestMessage(HttpMethod.Put, QueryHelpers.AddQueryString(uri, queryString))
var request = new HttpRequestMessage(HttpMethod.Get, QueryHelpers.AddQueryString(uri, queryString))
{
Headers = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } }
};
@@ -99,7 +97,17 @@ public class GiteeOAuthOptions : AdminOAuthOptions
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new Exception($"Failed to star repository: {response.StatusCode}, {content}");
var id = context.Identity.Claims.FirstOrDefault(a => a.Type == ClaimConst.VerificatId).Value;
var verificatInfoIds = _verificatInfoService.GetOne(id.ToLong());
_ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
await _noticeService.NavigationMesage(verificatInfoIds.ClientIds, "https://gitee.com/ThingsGateway/ThingsGateway", "创作不易如有帮助请star仓库").ConfigureAwait(false);
});
//throw new Exception($"Failed to star repository: {response.StatusCode}, {content}");
}

View File

@@ -0,0 +1,122 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Admin.Application;
public class GitHubOAuthOptions : AdminOAuthOptions
{
INoticeService _noticeService;
IVerificatInfoService _verificatInfoService;
public GitHubOAuthOptions() : base()
{
_noticeService = App.GetService<INoticeService>();
_verificatInfoService = App.GetService<IVerificatInfoService>();
SignInScheme = ClaimConst.Scheme;
AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
TokenEndpoint = "https://github.com/login/oauth/access_token";
UserInformationEndpoint = "https://api.github.com/user";
HomePath = "/";
CallbackPath = "/signin-github";
Scope.Add("read:user");
Scope.Add("public_repo"); // 需要用于 Star 仓库
Events.OnCreatingTicket = async context =>
{
await HandleGitHubStarAsync(context).ConfigureAwait(false);
};
Events.OnRedirectToAuthorizationEndpoint = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
Events.OnRemoteFailure = context =>
{
XTrace.WriteException(context.Failure);
return Task.CompletedTask;
};
}
protected override void ConfigureClaims()
{
ClaimActions.MapJsonKey(ClaimConst.AvatarUrl, "avatar_url");
ClaimActions.MapJsonKey(ClaimConst.Account, "login");
base.ConfigureClaims();
}
public override string GetName(JsonElement element)
{
if (element.TryGetProperty("login", out var loginProp))
{
return loginProp.GetString() ?? string.Empty;
}
return string.Empty;
}
private async Task HandleGitHubStarAsync(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
{
if (string.IsNullOrWhiteSpace(context.AccessToken))
throw new InvalidOperationException("Access token is missing.");
var request = new HttpRequestMessage(HttpMethod.Put, $"https://api.github.com/user/starred/{repoFullName}")
{
Headers =
{
Accept = { new MediaTypeWithQualityHeaderValue("application/vnd.github+json") },
Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken),
},
Content = new StringContent(string.Empty) // GitHub Star 接口需要空内容
};
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("ThingsGateway", "1.0")); // GitHub API 要求 User-Agent
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var id = context.Identity.Claims.FirstOrDefault(a => a.Type == ClaimConst.VerificatId).Value;
var verificatInfoIds = _verificatInfoService.GetOne(id.ToLong());
_ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
await _noticeService.NavigationMesage(verificatInfoIds.ClientIds, "https://github.com/ThingsGateway/ThingsGateway", "创作不易如有帮助请star仓库").ConfigureAwait(false);
});
}
}
/// <summary>处理用户信息方法</summary>
public override async Task<JsonElement> HandleUserInfoAsync(HttpContext context, OAuthTokenResponse tokens)
{
var request = new HttpRequestMessage(HttpMethod.Get, UserInformationEndpoint);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json"));
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("ThingsGateway", "1.0")); // GitHub API 要求 User-Agent
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)}");
}
}

View File

@@ -0,0 +1,6 @@
namespace ThingsGateway.Admin.Application;
public class GithubOAuthSettings : GiteeOAuthSettings
{
}

View File

@@ -0,0 +1,11 @@
using System.Text.Json;
namespace ThingsGateway.Admin.Application;
public static class OAuthUserExtensions
{
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

@@ -5,6 +5,9 @@
@<div>
<span class="mx-3">@item.ConfirmMessage</span>
<Button Text=@Localizers["Jump"] Color="Color.Link" OnClick="()=>NavigationManager.NavigateTo(item.Uri)"></Button>
<a href=@item.Uri target="_blank">
@item.Uri
</a>
</div>;
}

View File

@@ -33,7 +33,7 @@
</PopConfirmButton>
<PopConfirmButton Color=Color.Warning IsDisabled="SelectedRows.Count!=1||!AuthorizeButton(AdminOperConst.Edit)" Text=@OperDescLocalizer["ChangeParentResource"] Icon="fa fa-copy" OnConfirm="OnChangeParent">
<BodyTemplate>
<div class="min-height-500 overflow-y-auto">
<div class="overflow-y-auto" style="height:500px">
<TreeView Items="MenuTreeItems" IsVirtualize="true" OnTreeItemClick="a=>{ChangeParentId=a.Value.Id;return Task.CompletedTask;}" />
</div>
</BodyTemplate>

View File

@@ -16,6 +16,7 @@
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<CETCompat>false</CETCompat>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->

View File

@@ -39,7 +39,7 @@
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">

View File

@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<PluginVersion>10.7.56</PluginVersion>
<ProPluginVersion>10.7.56</ProPluginVersion>
<PluginVersion>10.7.58</PluginVersion>
<ProPluginVersion>10.7.58</ProPluginVersion>
<AuthenticationVersion>2.6.0</AuthenticationVersion>
<NET8Version>8.0.17</NET8Version>
<NET9Version>9.0.6</NET9Version>

View File

@@ -7,26 +7,24 @@
@namespace ThingsGateway.Gateway.Razor
<ValidateForm Model="Model.Value"
@key=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")
<ValidateForm Model="Model.Value"
@key=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")
@ref=Model.ValidateForm
Id=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")>
@ref=Model.ValidateForm
Id=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")
>
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems IsDisplay="!CanWrite" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=@(CanWrite?2:3) ShowLabelTooltip=true LabelWidth=@(CanWrite?240:120) Model="Model.Value" ShowLabel="true" @key=@($"DeviceEditEditorFormObject{Id}{Model.Value.GetType().TypeHandle.Value}")>
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems IsDisplay="!CanWrite" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=@(CanWrite ? 2 : 3) ShowLabelTooltip=true LabelWidth=@(CanWrite ? 240 : 120) Model="Model.Value" ShowLabel="true" @key=@($"DeviceEditEditorFormObject{Id}{Model.Value.GetType().TypeHandle.Value}")>
<FieldItems>
@if (Model.Value is BusinessPropertyWithCacheIntervalScript businessProperty)
{
<EditorItem FieldExpression=@(()=>context) Field=@(context)>
<EditorItem FieldExpression=@(() => context) Field=@(context)>
<EditTemplate Context="value">
<div class="col-12 col-md-12 min-height-500">
<BootstrapLabel Value=@PropertyComponentLocalizer["BigTextScriptDeviceModel"] ShowLabelTooltip="true" />
<CodeEditor ShowLineNo @bind-Value=@businessProperty.BigTextScriptDeviceModel Language="csharp" Theme="vs-dark" IsReadonly=@(!CanWrite) />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptDeviceModel))">
<Button IsDisabled=@(!CanWrite) OnClick=@(() => CheckScript(businessProperty, nameof(businessProperty.BigTextScriptDeviceModel), Localizer["check"], this, DialogService))>
@Localizer["Check"]
</Button>
</div>
@@ -36,7 +34,7 @@ Id=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")
<CodeEditor IsReadonly=@(!CanWrite) ShowLineNo @bind-Value=@businessProperty.BigTextScriptVariableModel Language="csharp" Theme="vs-dark" />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptVariableModel))">
<Button IsDisabled=@(!CanWrite) OnClick=@(() => CheckScript(businessProperty, nameof(businessProperty.BigTextScriptVariableModel), Localizer["check"], this, DialogService))>
@Localizer["Check"]
</Button>
</div>
@@ -46,7 +44,7 @@ Id=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")
<CodeEditor IsReadonly=@(!CanWrite) ShowLineNo @bind-Value=@businessProperty.BigTextScriptAlarmModel Language="csharp" Theme="vs-dark" />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptAlarmModel))">
<Button IsDisabled=@(!CanWrite) OnClick=@(() => CheckScript(businessProperty, nameof(businessProperty.BigTextScriptAlarmModel), Localizer["check"], this, DialogService))>
@Localizer["Check"]
</Button>
</div>

View File

@@ -8,6 +8,8 @@
// QQ群605534569
// ------------------------------------------------------------------------------
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Gateway.Razor;
public partial class PropertyComponent : IPropertyUIBase
{
@@ -32,7 +34,7 @@ public partial class PropertyComponent : IPropertyUIBase
[Inject]
private IStringLocalizer<DeviceEditComponent> Localizer { get; set; }
private async Task CheckScript(BusinessPropertyWithCacheIntervalScript businessProperty, string pname)
public static async Task CheckScript(BusinessPropertyWithCacheIntervalScript businessProperty, string pname, string title, object receiver, DialogService dialogService)
{
string script = null;
if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
@@ -56,7 +58,7 @@ public partial class PropertyComponent : IPropertyUIBase
var op = new DialogOption()
{
IsScrolling = true,
Title = Localizer["Check"],
Title = title,
ShowFooter = false,
ShowCloseButton = false,
Size = Size.ExtraExtraLarge,
@@ -65,8 +67,16 @@ public partial class PropertyComponent : IPropertyUIBase
op.Component = BootstrapDynamicComponent.CreateComponent<ScriptCheck>(new Dictionary<string, object?>
{
{nameof(ScriptCheck.Data),Array.Empty<object>() },
{nameof(ScriptCheck.Script),script },
{nameof(ScriptCheck.GetResult), (string input,string script)=>
{
var type= script == businessProperty.BigTextScriptDeviceModel?typeof(List<DeviceBasicData>):script ==businessProperty.BigTextScriptAlarmModel?typeof(List<AlarmVariable>):typeof(List<VariableBasicData>);
var data = (IEnumerable<object>)Newtonsoft.Json.JsonConvert.DeserializeObject(input, type);
var value = data.GetDynamicModel(script);
return Task.FromResult( value.ToSystemTextJsonString());
}},
{nameof(ScriptCheck.OnGetDemo),()=>
{
return
@@ -208,7 +218,7 @@ public partial class PropertyComponent : IPropertyUIBase
;
}
},
{nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v =>
{nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(receiver, v =>
{
if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
{
@@ -227,7 +237,7 @@ public partial class PropertyComponent : IPropertyUIBase
}) },
});
await DialogService.Show(op);
await dialogService.Show(op);
}

View File

@@ -8,17 +8,14 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Gateway.Razor;
public partial class ScriptCheck
{
private string Input { get; set; }
[Parameter, EditorRequired]
public string Input { get; set; } = string.Empty;
private string Output { get; set; }
[Parameter, EditorRequired]
public IEnumerable<object> Data { get; set; }
[Parameter, EditorRequired]
public string Script { get; set; }
[Parameter, EditorRequired]
@@ -30,21 +27,20 @@ public partial class ScriptCheck
await ScriptChanged.InvokeAsync(script);
}
private Type type;
protected override void OnInitialized()
{
Input = Data.ToSystemTextJsonString();
type = Data.GetType();
base.OnInitialized();
}
private void CheckScript()
[Parameter]
public Func<string, string, Task<string>> GetResult { get; set; }
private async Task CheckScript()
{
try
{
Data = (IEnumerable<object>)Newtonsoft.Json.JsonConvert.DeserializeObject(Input, type);
var value = Data.GetDynamicModel(Script);
Output = value.ToSystemTextJsonString();
if (GetResult != null)
{
Output = await GetResult(Input, Script).ConfigureAwait(false);
}
}
catch (Exception ex)

View File

@@ -1,38 +0,0 @@
@using BootstrapBlazor.Components
@using Microsoft.Extensions.Localization
@using ThingsGateway.Extension
@using ThingsGateway.Foundation
@using ThingsGateway.Admin.Application
@using ThingsGateway.Admin.Razor
@using ThingsGateway.Gateway.Application
@using ThingsGateway.Plugin.SqlDB
@namespace ThingsGateway.Plugin.DB
<ValidateForm Model="Model.Value"
@key=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")
@ref=Model.ValidateForm
Id=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")>
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems IsDisplay="!CanWrite" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=@(CanWrite?2:3) ShowLabelTooltip=true LabelWidth=@(CanWrite?240:120) Model="Model.Value" ShowLabel="true" @key=@($"DeviceEditEditorFormObject{Id}{Model.Value.GetType().TypeHandle.Value}")>
<FieldItems>
@if (Model.Value is RealDBProducerProperty businessProperty)
{
<EditorItem FieldExpression=@(()=>context) Field=@(context)>
<EditTemplate Context="value">
<div class="col-12 col-md-12">
<BootstrapLabel Value=@RealDBProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
<CodeEditor @bind-Value=@businessProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
</div>
</EditTemplate>
</EditorItem>
}
</FieldItems>
</EditorFormObject>
</ValidateForm>

View File

@@ -1,41 +0,0 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using ThingsGateway.Razor;
namespace ThingsGateway.Plugin.DB
{
public partial class RealDBProducerPropertyRazor : IPropertyUIBase
{
[Parameter, EditorRequired]
public IEnumerable<IEditorItem> PluginPropertyEditorItems { get; set; }
[Parameter, EditorRequired]
public string Id { get; set; }
[Parameter, EditorRequired]
public bool CanWrite { get; set; }
[Parameter, EditorRequired]
public ModelValueValidateForm Model { get; set; }
IStringLocalizer RealDBProducerPropertyLocalizer { get; set; }
protected override Task OnParametersSetAsync()
{
RealDBProducerPropertyLocalizer = App.CreateLocalizerByType(Model.Value.GetType());
return base.OnParametersSetAsync();
}
}
}

View File

@@ -13,6 +13,7 @@ using BootstrapBlazor.Components;
using Mapster;
using ThingsGateway.Admin.Application;
using ThingsGateway.Debug;
using ThingsGateway.Foundation;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension;
@@ -30,7 +31,7 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariableMode
private readonly QuestDBProducerVariableProperty _variablePropertys = new();
/// <inheritdoc/>
public override Type DriverPropertyUIType => typeof(RealDBProducerPropertyRazor);
public override Type DriverPropertyUIType => typeof(SqlDBProducerPropertyRazor);
/// <inheritdoc/>
public override Type DriverUIType

View File

@@ -5,6 +5,7 @@
@using ThingsGateway.Admin.Application
@using ThingsGateway.Admin.Razor
@using ThingsGateway.Gateway.Application
@using ThingsGateway.Plugin.DB
@using ThingsGateway.Plugin.SqlDB
@namespace ThingsGateway.Debug
@@ -23,7 +24,7 @@
<EditTemplate Context="value">
<div class="col-12 col-md-12">
<BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
<CodeEditor @bind-Value=@businessProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptHistoryTable))">
@@ -32,7 +33,7 @@
</div>
</div>
<div class="col-12 col-md-12">
<BootstrapLabel Value=@SqlDBProducerPropertyLocalizer["BigTextScriptRealTable"] ShowLabelTooltip="true" />
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptRealTable"] ShowLabelTooltip="true" />
<CodeEditor @bind-Value=@businessProperty.BigTextScriptRealTable Language="csharp" Theme="vs-dark" />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptRealTable))">
@@ -43,6 +44,24 @@
</EditTemplate>
</EditorItem>
}
else if (Model.Value is RealDBProducerProperty realDBProducerProperty)
{
<EditorItem FieldExpression=@(() => context) Field=@(context)>
<EditTemplate Context="value">
<div class="col-12 col-md-12">
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
<CodeEditor @bind-Value=@realDBProducerProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="() => CheckScript(realDBProducerProperty, nameof(realDBProducerProperty.BigTextScriptHistoryTable))">
@RazorLocalizer["Check"]
</Button>
</div>
</div>
</EditTemplate>
</EditorItem>
}
</FieldItems>
</EditorFormObject>

View File

@@ -14,9 +14,16 @@ using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using System.Text;
using ThingsGateway.Gateway.Razor;
using ThingsGateway.Plugin.DB;
using ThingsGateway.Plugin.SqlDB;
using ThingsGateway.Plugin.TDengineDB;
using ThingsGateway.Razor;
using ThingsGateway.SqlSugar;
using TouchSocket.Core;
namespace ThingsGateway.Debug
{
@@ -35,11 +42,11 @@ namespace ThingsGateway.Debug
[Parameter, EditorRequired]
public ModelValueValidateForm Model { get; set; }
IStringLocalizer SqlDBProducerPropertyLocalizer { get; set; }
IStringLocalizer ProducerPropertyLocalizer { get; set; }
protected override Task OnParametersSetAsync()
{
SqlDBProducerPropertyLocalizer = App.CreateLocalizerByType(Model.Value.GetType());
ProducerPropertyLocalizer = App.CreateLocalizerByType(Model.Value.GetType());
return base.OnParametersSetAsync();
}
@@ -65,9 +72,24 @@ namespace ThingsGateway.Debug
op.Component = BootstrapDynamicComponent.CreateComponent<ScriptCheck>(new Dictionary<string, object?>
{
{nameof(ScriptCheck.Data),Array.Empty < object >() },
{nameof(ScriptCheck.Script),script },
{nameof(ScriptCheck.OnGetDemo),()=>
{nameof(ScriptCheck.GetResult), async (string input,string script)=>
{
var type= typeof(List<VariableBasicData>);
var data = (IEnumerable<object>)Newtonsoft.Json.JsonConvert.DeserializeObject(input, type);
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(script);
StringBuilder stringBuilder=new($"Compilation successful{Environment.NewLine}");
getDeviceModel.Logger=new EasyLogger(a=>stringBuilder.AppendLine(a));
using var db = SqlDBBusinessDatabaseUtil.GetDb(businessProperty);
await getDeviceModel.DBInit(db,default);
await getDeviceModel.DBInsertable(db,data,default);
return stringBuilder.ToString();
}},
{nameof(ScriptCheck.OnGetDemo),()=>
{
return
pname == nameof(SqlDBProducerProperty.BigTextScriptHistoryTable)?
@@ -153,6 +175,134 @@ namespace ThingsGateway.Debug
await DialogService.Show(op);
}
private async Task CheckScript(RealDBProducerProperty businessProperty, string pname)
{
string script = businessProperty.BigTextScriptHistoryTable;
var op = new DialogOption()
{
IsScrolling = true,
Title = RazorLocalizer["Check"],
ShowFooter = false,
ShowCloseButton = false,
Size = Size.ExtraExtraLarge,
FullScreenSize = FullScreenSize.None
};
op.Component = BootstrapDynamicComponent.CreateComponent<ScriptCheck>(new Dictionary<string, object?>
{
{nameof(ScriptCheck.Script),script },
{nameof(ScriptCheck.GetResult), async (string input,string script)=>
{
var type= typeof(List<VariableBasicData>);
var data = (IEnumerable<object>)Newtonsoft.Json.JsonConvert.DeserializeObject(input, type);
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(script);
StringBuilder stringBuilder=new($"Compilation successful{Environment.NewLine}");
getDeviceModel.Logger=new EasyLogger(a=>stringBuilder.AppendLine(a));
SqlSugarClient db=null;
if(businessProperty.DbType==SqlSugar.DbType.TDengine)
db = TDengineDBUtil.GetDb(businessProperty.DbType, businessProperty.BigTextConnectStr, businessProperty.TableNameLow);
else
db = BusinessDatabaseUtil.GetDb(businessProperty.DbType, businessProperty.BigTextConnectStr);
await getDeviceModel.DBInit(db,default);
await getDeviceModel.DBInsertable(db,data,default);
return stringBuilder.ToString();
}},
{nameof(ScriptCheck.OnGetDemo),()=>
{
return
pname == nameof(SqlDBProducerProperty.BigTextScriptHistoryTable)?
""""
using ThingsGateway.Foundation;
using System.Dynamic;
using ThingsGateway.SqlSugar;
using ThingsGateway.Gateway.Application;
using ThingsGateway.Plugin.DB;
using System.Dynamic;
using TouchSocket.Core;
public class S1 : DynamicSQLBase
{
public override async Task DBInit(ISqlSugarClient db, CancellationToken cancellationToken)
{
var sql = $"""
111
""";
await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false);
}
public override async Task DBInsertable(ISqlSugarClient db, IEnumerable<object> datas, CancellationToken cancellationToken)
{
var sql = $"""
111
""";
await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
""""
:
pname == nameof(SqlDBProducerProperty.BigTextScriptRealTable)?
""""
using System.Dynamic;
using ThingsGateway.Foundation;
using ThingsGateway.SqlSugar;
using ThingsGateway.Gateway.Application;
using ThingsGateway.Plugin.DB;
using TouchSocket.Core;
public class S1 : DynamicSQLBase
{
public override async Task DBInit(ISqlSugarClient db, CancellationToken cancellationToken)
{
var sql = $"""
111
""";
await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false);
}
public override async Task DBInsertable(ISqlSugarClient db, IEnumerable<object> datas, CancellationToken cancellationToken)
{
var sql = $"""
111
""";
await db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
""""
:
""
;
}
},
{nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v =>
{
businessProperty.BigTextScriptHistoryTable=v;
}) },
});
await DialogService.Show(op);
}
[Inject]
DialogService DialogService { get; set; }
}

View File

@@ -17,6 +17,7 @@ using Newtonsoft.Json.Linq;
using System.Reflection;
using ThingsGateway.Admin.Application;
using ThingsGateway.Debug;
using ThingsGateway.Foundation;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension;
@@ -37,7 +38,7 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariableM
};
private readonly TDengineDBProducerVariableProperty _variablePropertys = new();
/// <inheritdoc/>
public override Type DriverPropertyUIType => typeof(RealDBProducerPropertyRazor);
public override Type DriverPropertyUIType => typeof(SqlDBProducerPropertyRazor);
/// <inheritdoc/>
public override Type DriverUIType

View File

@@ -5,6 +5,7 @@
@using ThingsGateway.Admin.Application
@using ThingsGateway.Admin.Razor
@using ThingsGateway.Gateway.Application
@using ThingsGateway.Gateway.Razor
@namespace ThingsGateway.Plugin.Mqtt
@@ -55,7 +56,7 @@
<BootstrapLabel Value=@Localizer["BigTextScriptDeviceModel"] ShowLabelTooltip="true" />
<CodeEditor ShowLineNo @bind-Value=@businessProperty.BigTextScriptDeviceModel Language="csharp" Theme="vs-dark" IsReadonly=@(!CanWrite) />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptDeviceModel))">
<Button IsDisabled=@(!CanWrite) OnClick=@(()=>PropertyComponent.CheckScript(businessProperty,nameof(businessProperty.BigTextScriptDeviceModel), Localizer["check"], this, DialogService))>
@Localizer["Check"]
</Button>
</div>
@@ -65,7 +66,7 @@
<CodeEditor IsReadonly=@(!CanWrite) ShowLineNo @bind-Value=@businessProperty.BigTextScriptVariableModel Language="csharp" Theme="vs-dark" />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptVariableModel))">
<Button IsDisabled=@(!CanWrite) OnClick=@(()=>PropertyComponent.CheckScript(businessProperty,nameof(businessProperty.BigTextScriptVariableModel), Localizer["check"], this, DialogService))>
@Localizer["Check"]
</Button>
</div>
@@ -75,7 +76,7 @@
<CodeEditor IsReadonly=@(!CanWrite) ShowLineNo @bind-Value=@businessProperty.BigTextScriptAlarmModel Language="csharp" Theme="vs-dark" />
<div class="ms-2 d-flex justify-content-center align-items-center">
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptAlarmModel))">
<Button IsDisabled=@(!CanWrite) OnClick=@(()=>PropertyComponent.CheckScript(businessProperty,nameof(businessProperty.BigTextScriptAlarmModel), Localizer["check"], this, DialogService))>
@Localizer["Check"]
</Button>
</div>

View File

@@ -14,7 +14,6 @@ using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using ThingsGateway.Gateway.Razor;
using ThingsGateway.Razor;
namespace ThingsGateway.Plugin.Mqtt
@@ -134,207 +133,6 @@ namespace ThingsGateway.Plugin.Mqtt
private async Task CheckScript(BusinessPropertyWithCacheIntervalScript businessProperty, string pname)
{
string script = null;
if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
{
script = businessProperty.BigTextScriptAlarmModel;
}
else if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptVariableModel))
{
script = businessProperty.BigTextScriptVariableModel;
}
else if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel))
{
script = businessProperty.BigTextScriptDeviceModel;
}
else
{
return;
}
var op = new DialogOption()
{
IsScrolling = true,
Title = Localizer["Check"],
ShowFooter = false,
ShowCloseButton = false,
Size = Size.ExtraExtraLarge,
FullScreenSize = FullScreenSize.None
};
op.Component = BootstrapDynamicComponent.CreateComponent<ScriptCheck>(new Dictionary<string, object?>
{
{nameof(ScriptCheck.Data),Array.Empty<object>() },
{nameof(ScriptCheck.Script),script },
{nameof(ScriptCheck.OnGetDemo),()=>
{
return
pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel)?
"""
using ThingsGateway.Foundation;
using System.Dynamic;
using TouchSocket.Core;
public class S1 : IDynamicModel
{
public IEnumerable<dynamic> GetList(IEnumerable<object> datas)
{
List<ExpandoObject> deviceObjs = new List<ExpandoObject>();
foreach (var v in datas)
{
var device = (DeviceBasicData)v;
var expando = new ExpandoObject();
var deviceObj = new ExpandoObject();
deviceObj.TryAdd(nameof(Device.Description), device.Description);
deviceObj.TryAdd(nameof(DeviceBasicData.ActiveTime), device.ActiveTime);
deviceObj.TryAdd(nameof(DeviceBasicData.DeviceStatus), device.DeviceStatus.ToString());
deviceObj.TryAdd(nameof(DeviceBasicData.LastErrorMessage), device.LastErrorMessage);
deviceObj.TryAdd(nameof(DeviceBasicData.PluginName), device.PluginName);
deviceObj.TryAdd(nameof(DeviceBasicData.Remark1), device.Remark1);
deviceObj.TryAdd(nameof(DeviceBasicData.Remark2), device.Remark2);
deviceObj.TryAdd(nameof(DeviceBasicData.Remark3), device.Remark3);
deviceObj.TryAdd(nameof(DeviceBasicData.Remark4), device.Remark4);
deviceObj.TryAdd(nameof(DeviceBasicData.Remark5), device.Remark5);
expando.TryAdd(nameof(Device.Name), deviceObj);
}
return deviceObjs;
}
}
"""
:
pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptVariableModel)?
"""
using ThingsGateway.Foundation;
using System.Dynamic;
using TouchSocket.Core;
public class S2 : IDynamicModel
{
public IEnumerable<dynamic> GetList(IEnumerable<object> datas)
{
List<ExpandoObject> deviceObjs = new List<ExpandoObject>();
//按设备名称分组
var groups = datas.Where(a => !string.IsNullOrEmpty(((VariableBasicData)a).DeviceName)).GroupBy(a => ((VariableBasicData)a).DeviceName, a => ((VariableBasicData)a));
foreach (var group in groups)
{
//按采集时间分组
var data = group.GroupBy(a => a.CollectTime.DateTimeToUnixTimestamp());
var deviceObj = new ExpandoObject();
List<ExpandoObject> expandos = new List<ExpandoObject>();
foreach (var item in data)
{
var expando = new ExpandoObject();
expando.TryAdd("ts", item.Key);
var variableObj = new ExpandoObject();
foreach (var tag in item)
{
variableObj.TryAdd(tag.Name, tag.Value);
}
expando.TryAdd("values", variableObj);
expandos.Add(expando);
}
deviceObj.TryAdd(group.Key, expandos);
deviceObjs.Add(deviceObj);
}
return deviceObjs;
}
}
"""
:
pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel)?
"""
using TouchSocket.Core;
public class DeviceScript : IDynamicModel
{
public IEnumerable<dynamic> GetList(IEnumerable<object> datas)
{
List<ExpandoObject> deviceObjs = new List<ExpandoObject>();
//按设备名称分组
var groups = datas.Where(a => !string.IsNullOrEmpty(((AlarmVariable)a).DeviceName)).GroupBy(a => ((AlarmVariable)a).DeviceName, a => ((AlarmVariable)a));
foreach (var group in groups)
{
//按采集时间分组
var data = group.GroupBy(a => a.AlarmTime.DateTimeToUnixTimestamp());
var deviceObj = new ExpandoObject();
List<ExpandoObject> expandos = new List<ExpandoObject>();
foreach (var item in data)
{
var expando = new ExpandoObject();
expando.TryAdd("ts", item.Key);
var variableObj = new ExpandoObject();
foreach (var tag in item)
{
var alarmObj = new ExpandoObject();
alarmObj.TryAdd(nameof(tag.AlarmCode), tag.AlarmCode);
alarmObj.TryAdd(nameof(tag.AlarmText), tag.AlarmText);
alarmObj.TryAdd(nameof(tag.AlarmType), tag.AlarmType);
alarmObj.TryAdd(nameof(tag.AlarmLimit), tag.AlarmLimit);
alarmObj.TryAdd(nameof(tag.EventTime), tag.EventTime);
alarmObj.TryAdd(nameof(tag.EventType), tag.EventType);
variableObj.TryAdd(tag.Name, alarmObj);
}
expando.TryAdd("alarms", variableObj);
expandos.Add(expando);
}
deviceObj.TryAdd(group.Key, expandos);
deviceObjs.Add(deviceObj);
}
return deviceObjs;
}
}
"""
:
""
;
}
},
{nameof(ScriptCheck.ScriptChanged),EventCallback.Factory.Create<string>(this, v =>
{
if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel))
{
businessProperty.BigTextScriptAlarmModel=v;
}
else if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptVariableModel))
{
businessProperty.BigTextScriptVariableModel=v;
}
else if (pname == nameof(BusinessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel))
{
businessProperty.BigTextScriptDeviceModel=v;
}
}) },
});
await DialogService.Show(op);
}
[Inject]
private DialogService DialogService { get; set; }
}

View File

@@ -50,6 +50,7 @@
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<!--<PublishAot>true</PublishAot>-->
<CETCompat>false</CETCompat>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>

View File

@@ -0,0 +1,10 @@
{
"GiteeOAuthSettings": {
"ClientId": "237adcc9fd39a71d53691d96642e1c2432db90ad93c02f72604897a348f347c6",
"ClientSecret": "4650577daf4cb02b325f3ae163e6706e87c4dc2ad615b035821b21116b279bca"
},
"GithubOAuthSettings": {
"ClientId": "Ov23lijDvpmRPawSDyWN",
"ClientSecret": "1f4aab562651dbe5d6a637afc57a5ca9a397ac07"
}
}

View File

@@ -48,7 +48,8 @@
<h5 class="mt-2 mb-12 ">@Localizer["Welcome"] 👋</h5>
@if(WebsiteOption.Value.Demo)
{
<Button Class="btn-block mt-5" IsAsync OnClick=GiteeLogin>@Localizer["GiteeLogin"]</Button>
<Button class="btn-block mt-5" IsAsync OnClick=GithubLogin>@Localizer["GithubLogin"]</Button>
<Button class="btn-block mt-5" IsAsync OnClick=GiteeLogin>@Localizer["GiteeLogin"]</Button>
}
else
{

View File

@@ -64,14 +64,25 @@ public partial class Login
_versionString = $"v{VersionService.Version}";
return base.OnInitializedAsync();
}
private void GiteeLogin()
{
var websiteOptions = App.GetOptions<WebsiteOptions>()!;
if (websiteOptions.Demo)
{
NavigationManager.NavigateTo("/api/auth/oauth-login", forceLoad: true);
NavigationManager.NavigateTo("/api/auth/oauth-login?scheme=Gitee", forceLoad: true);
}
}
private void GithubLogin()
{
var websiteOptions = App.GetOptions<WebsiteOptions>()!;
if (websiteOptions.Demo)
{
NavigationManager.NavigateTo("/api/auth/oauth-login?scheme=Github", forceLoad: true);
}
}
[Inject]
NavigationManager NavigationManager { get; set; }
private async Task LoginAsync(EditContext context)

View File

@@ -35,6 +35,7 @@
},
"ThingsGateway.Server.Login": {
"GiteeLogin": "Gitee OAuth",
"GithubLogin": "Github OAuth",
"LoginErrorc2": "Please contact the administrator!",
"LoginErrorh1": "Login Error",
"LoginErrorh2": "Login Failed",

View File

@@ -35,6 +35,7 @@
},
"ThingsGateway.Server.Login": {
"GiteeLogin": "Gitee授权登录",
"GithubLogin": "Github授权登录",
"LoginErrorc2": "请联系管理员!",
"LoginErrorh1": "登录异常",
"LoginErrorh2": "登录失败",

View File

@@ -296,6 +296,14 @@ public class Startup : AppStartup
options.ClientSecret = data.ClientSecret;
});
authenticationBuilder.AddOAuth<GitHubOAuthOptions, AdminOAuthHandler<GitHubOAuthOptions>>("Github", "Github", options =>
{
var data = App.GetConfig<GithubOAuthSettings>("GithubOAuthSettings");
options.ClientId = data.ClientId;
options.ClientSecret = data.ClientSecret;
});
}
// 添加jwt授权

View File

@@ -51,6 +51,9 @@
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<CETCompat>false</CETCompat>
<!--<TieredCompilation>false</TieredCompilation>-->
<!--使用自托管线程池-->

View File

@@ -13,6 +13,7 @@
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<CETCompat>false</CETCompat>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>10.7.56</Version>
<Version>10.7.58</Version>
</PropertyGroup>
<ItemGroup>