Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d33d900592 | ||
|
|
29365c4ef9 | ||
|
|
17a6189089 |
@@ -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; } = "/";
|
||||
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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}");
|
||||
}
|
||||
|
||||
|
||||
@@ -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)}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public class GithubOAuthSettings : GiteeOAuthSettings
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<!--动态适用GC-->
|
||||
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
|
||||
<CETCompat>false</CETCompat>
|
||||
<!--使用自托管线程池-->
|
||||
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
|
||||
|
||||
|
||||
@@ -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' ">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
||||
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||
<!--<PublishAot>true</PublishAot>-->
|
||||
<CETCompat>false</CETCompat>
|
||||
|
||||
<!--动态适用GC-->
|
||||
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
|
||||
|
||||
10
src/ThingsGateway.Server/Configuration/OAuthSettings.json
Normal file
10
src/ThingsGateway.Server/Configuration/OAuthSettings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"GiteeOAuthSettings": {
|
||||
"ClientId": "237adcc9fd39a71d53691d96642e1c2432db90ad93c02f72604897a348f347c6",
|
||||
"ClientSecret": "4650577daf4cb02b325f3ae163e6706e87c4dc2ad615b035821b21116b279bca"
|
||||
},
|
||||
"GithubOAuthSettings": {
|
||||
"ClientId": "Ov23lijDvpmRPawSDyWN",
|
||||
"ClientSecret": "1f4aab562651dbe5d6a637afc57a5ca9a397ac07"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
},
|
||||
"ThingsGateway.Server.Login": {
|
||||
"GiteeLogin": "Gitee OAuth",
|
||||
"GithubLogin": "Github OAuth",
|
||||
"LoginErrorc2": "Please contact the administrator!",
|
||||
"LoginErrorh1": "Login Error",
|
||||
"LoginErrorh2": "Login Failed",
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
},
|
||||
"ThingsGateway.Server.Login": {
|
||||
"GiteeLogin": "Gitee授权登录",
|
||||
"GithubLogin": "Github授权登录",
|
||||
"LoginErrorc2": "请联系管理员!",
|
||||
"LoginErrorh1": "登录异常",
|
||||
"LoginErrorh2": "登录失败",
|
||||
|
||||
@@ -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授权
|
||||
|
||||
@@ -51,6 +51,9 @@
|
||||
<!--动态适用GC-->
|
||||
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
|
||||
|
||||
|
||||
<CETCompat>false</CETCompat>
|
||||
|
||||
<!--<TieredCompilation>false</TieredCompilation>-->
|
||||
|
||||
<!--使用自托管线程池-->
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
<!--动态适用GC-->
|
||||
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
|
||||
<CETCompat>false</CETCompat>
|
||||
<!--使用自托管线程池-->
|
||||
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>10.7.56</Version>
|
||||
<Version>10.7.58</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user