release:6.0.5.9

refactor: 封装furion swagger文档
refactor:简化代码
refactor:更新依赖包
This commit is contained in:
Diego
2024-08-18 04:03:01 +08:00
parent a9e0e0ead6
commit 1fd6cc2b5e
124 changed files with 3295 additions and 1236 deletions

View File

@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using ThingsGateway.ASPNetCore;
using ThingsGateway.NewLife.X;
namespace ThingsGateway.Admin.Application;

View File

@@ -12,6 +12,8 @@ using SqlSugar;
using System.Collections.Concurrent;
using ThingsGateway.ASPNetCore;
using ThingsGateway.Core;
using ThingsGateway.Core.Extension;
using ThingsGateway.Core.Json.Extension;
using ThingsGateway.Logging;

View File

@@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc;
namespace ThingsGateway.Admin.Application;
[ApiDescriptionSettings(false)]
[Route("api/auth")]
[LoggingMonitor]
public class AuthController : ControllerBase

View File

@@ -19,6 +19,7 @@ namespace ThingsGateway.Admin.Application;
/// <summary>
/// 文化 Controller
/// </summary>
[ApiDescriptionSettings(false)]
[Route("[controller]/[action]")]
public class CultureController : Controller
{

View File

@@ -16,6 +16,7 @@ namespace ThingsGateway.Admin.Application;
/// <summary>
/// 文件下载
/// </summary>
[ApiDescriptionSettings(false)]
[Route("api/file")]
public class FileController : ControllerBase
{

View File

@@ -1,82 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using ThingsGateway.Core;
using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// Gitee WebHook
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class GiteeController : ControllerBase
{
/// <summary>
/// 跨域握手协议
/// </summary>
/// <returns></returns>
[HttpOptions]
public string Options() => string.Empty;
/// <summary>
/// Gitee Webhook
/// </summary>
/// <returns></returns>
[HttpPost]
public IActionResult Webhook([FromQuery] string? id, [FromServices] IConfiguration config, [FromServices] IDispatchService<GiteePostBody> dispatch, [FromBody] GiteePostBody payload)
{
bool ret = false;
if (Check())
{
// 全局推送
if (payload.HeadCommit != null || payload.Commits?.Count > 0)
{
dispatch.Dispatch(new DispatchEntry<GiteePostBody>()
{
Name = "Gitee",
Entry = payload
});
}
ret = true;
}
return ret ? Ok() : Unauthorized();
bool Check()
{
var configId = config.GetValue<string>("WebHooks:Gitee:Id");
var configToken = config.GetValue<string>("WebHooks:Gitee:Token");
var token = "";
if (Request.Headers.TryGetValue("X-Gitee-Token", out var val))
{
token = val.FirstOrDefault() ?? string.Empty;
}
return id == configId && token == configToken
&& payload.Id == configId && payload.Password == configToken;
}
}
/// <summary>
/// Webhook 测试接口
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Webhook()
{
return Ok(new { Message = "Ok" });
}
}

View File

@@ -13,8 +13,16 @@ using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 登录
/// </summary>
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
[Description("登录")]
[Route("openapi/auth")]
[LoggingMonitor]
public class OpenApiController : ControllerBase
@@ -27,6 +35,7 @@ public class OpenApiController : ControllerBase
}
[HttpPost("login")]
[DisplayName("登录")]
[AllowAnonymous]
public async Task<OpenApiLoginOutput> LoginAsync([FromBody] OpenApiLoginInput input)
{
@@ -39,6 +48,7 @@ public class OpenApiController : ControllerBase
[HttpPost("logout")]
[Authorize]
[DisplayName("登出")]
[IgnoreRolePermission]
public async Task LogoutAsync()
{

View File

@@ -1,28 +1,28 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
//using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
//using Microsoft.AspNetCore.Authorization;
//using Microsoft.AspNetCore.Mvc;
namespace ThingsGateway.Admin.Application;
//namespace ThingsGateway.Admin.Application;
[Route("api/[controller]/[action]")]
[RolePermission]
[Authorize(AuthenticationSchemes = "Bearer")]
public class TestController : ControllerBase
{
[HttpGet]
public async Task Test(QueryPageOptions queryPageOptions)
{
await Task.CompletedTask;
}
}
//[Route("api/[controller]/[action]")]
//[RolePermission]
//[Authorize(AuthenticationSchemes = "Bearer")]
//public class TestController : ControllerBase
//{
// [HttpGet]
// public async Task Test(QueryPageOptions queryPageOptions)
// {
// await Task.CompletedTask;
// }
//}

View File

@@ -14,6 +14,8 @@ using Microsoft.AspNetCore.WebUtilities;
using System.Net;
using System.Security.Claims;
using ThingsGateway.ASPNetCore;
using UAParser;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,6 +10,7 @@
using BootstrapBlazor.Components;
using ThingsGateway.Core;
using ThingsGateway.Core.Json.Extension;
namespace ThingsGateway.Admin.Application;

View File

@@ -17,6 +17,7 @@ using SqlSugar;
using System.Security.Claims;
using ThingsGateway.ASPNetCore;
using ThingsGateway.Core;
using Yitter.IdGenerator;

View File

@@ -1,6 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="$(SolutionDir)PackNuget.props" />
<PropertyGroup>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>ThingsGateway.Admin.ASPNetCore.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />

View File

@@ -0,0 +1,324 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>ThingsGateway.Admin.ASPNetCore</name>
</assembly>
<members>
<member name="T:ThingsGateway.Admin.Application.BlazorAuthenticationStateProvider">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Admin.Application.BlazorAuthenticationStateProvider.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Admin.Application.BlazorAuthenticationStateProvider.PipelineAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext,Microsoft.AspNetCore.Http.DefaultHttpContext)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Admin.Application.BlazorAuthenticationStateProvider.CheckVerificatFromCacheAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext)">
<summary>
检查 BearerToken/Cookie 有效性
</summary>
<param name="context">DefaultHttpContext</param>
<returns></returns>
</member>
<member name="T:ThingsGateway.Admin.Application.DatabaseLoggingWriter">
<summary>
数据库写入器
</summary>
</member>
<member name="F:ThingsGateway.Admin.Application.DatabaseLoggingWriter._operateLogMessageQueue">
<summary>
日志消息队列(线程安全)
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.DatabaseLoggingWriter.WriteAsync(ThingsGateway.Logging.LogMessage,System.Boolean)">
<summary>
此方法只会写入经由MVCFilter捕捉的方法日志对于BlazorServer的内部操作<see cref="T:ThingsGateway.Admin.Application.OperDescAttribute"/>执行
</summary>
<param name="logMsg"></param>
<param name="flush"></param>
</member>
<member name="M:ThingsGateway.Admin.Application.DatabaseLoggingWriter.CreateOperationLog(System.String,System.String,ThingsGateway.ASPNetCore.LoggingMonitorJson,UAParser.ClientInfo,System.Boolean)">
<summary>
创建操作日志
</summary>
<param name="operation">操作名称</param>
<param name="path">请求地址</param>
<param name="loggingMonitor">loggingMonitor</param>
<param name="clientInfo">客户端信息</param>
<param name="flush"></param>
<returns></returns>
</member>
<member name="M:ThingsGateway.Admin.Application.DatabaseLoggingWriter.CreateVisitLog(System.String,System.String,ThingsGateway.ASPNetCore.LoggingMonitorJson,UAParser.ClientInfo,System.Boolean)">
<summary>
创建访问日志
</summary>
<param name="operation">访问类型</param>
<param name="path"></param>
<param name="loggingMonitor">loggingMonitor</param>
<param name="clientInfo">客户端信息</param>
<param name="flush"></param>
</member>
<member name="T:ThingsGateway.Admin.Application.CultureController">
<summary>
文化 Controller
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.CultureController.ResetCulture(System.String)">
<summary>
重置文化方法
</summary>
<param name="redirectUri"></param>
<returns></returns>
</member>
<member name="M:ThingsGateway.Admin.Application.CultureController.SetCulture(System.String,System.String)">
<summary>
设置文化方法
</summary>
<param name="culture"></param>
<param name="redirectUri"></param>
<returns></returns>
</member>
<member name="T:ThingsGateway.Admin.Application.FileController">
<summary>
文件下载
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.FileController.Download(System.String)">
<summary>
下载wwwroot文件夹下的文件
</summary>
<param name="fileName">相对路径</param>
<returns></returns>
</member>
<member name="T:ThingsGateway.Admin.Application.OpenApiController">
<summary>
登录
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthRazorService.LoginAsync(ThingsGateway.Admin.Application.LoginInput)">
<summary>
用户登录
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthRazorService.LoginOutAsync">
<summary>
注销当前用户
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.LoginAsync(ThingsGateway.Admin.Application.LoginInput,System.Boolean)">
<summary>
登录
</summary>
<param name="input">登录参数</param>
<param name="isCookie">cookie方式登录</param>
<returns>登录输出</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.LoginOutAsync">
<summary>
注销当前用户
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.BeforeLogin(ThingsGateway.Admin.Application.AppConfig,System.String)">
<summary>
登录之前执行的方法
</summary>
<param name="appConfig">配置</param>
<param name="userName">用户名称</param>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.ExecLogin(ThingsGateway.Admin.Application.LoginPolicy,ThingsGateway.Admin.Application.LoginInput,ThingsGateway.Admin.Application.SysUser,System.Boolean)">
<summary>
执行登录
</summary>
<param name="loginPolicy">登录策略</param>
<param name="input">用户登录参数</param>
<param name="sysUser">用户信息</param>
<param name="isCookie">cookie方式登录</param>
<returns>登录输出结果</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.LoginError(ThingsGateway.Admin.Application.LoginPolicy,System.String)">
<summary>
登录错误反馈
</summary>
<param name="loginPolicy">登录策略</param>
<param name="userName">用户名称</param>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.RemoveTokenFromCache(ThingsGateway.Admin.Application.LoginEvent)">
<summary>
从cache删除用户verificat
</summary>
<param name="loginEvent">登录事件参数</param>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.SingleLogin(System.Int64)">
<summary>
单用户登录通知用户下线
</summary>
<param name="userId">用户Id</param>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.UpdateUser(ThingsGateway.Admin.Application.LoginEvent)">
<summary>
登录事件
</summary>
<param name="loginEvent"></param>
<returns></returns>
</member>
<member name="M:ThingsGateway.Admin.Application.AuthService.WriteTokenToCache(ThingsGateway.Admin.Application.LoginPolicy,ThingsGateway.Admin.Application.LoginEvent)">
<summary>
写入用户verificat到cache
</summary>
<param name="loginPolicy">登录策略</param>
<param name="loginEvent">登录事件参数</param>
</member>
<member name="T:ThingsGateway.Admin.Application.LoginEvent">
<summary>
登录事件参数
</summary>
</member>
<member name="F:ThingsGateway.Admin.Application.LoginEvent.DateTime">
<summary>
时间
</summary>
</member>
<member name="P:ThingsGateway.Admin.Application.LoginEvent.Device">
<summary>
登录设备
</summary>
</member>
<member name="P:ThingsGateway.Admin.Application.LoginEvent.Expire">
<summary>
过期时间
</summary>
</member>
<member name="P:ThingsGateway.Admin.Application.LoginEvent.Ip">
<summary>
Ip地址
</summary>
</member>
<member name="P:ThingsGateway.Admin.Application.LoginEvent.SysUser">
<summary>
用户信息
</summary>
</member>
<member name="P:ThingsGateway.Admin.Application.LoginEvent.VerificatId">
<summary>
VerificatId
</summary>
</member>
<member name="M:ThingsGateway.Admin.Application.FileService.GetFileStreamResult(System.String,System.String,System.Boolean)">
<summary>
获取本地存储文件流
</summary>
<param name="path">文件夹</param>
<param name="fileName">文件名称</param>
<param name="isPathFolder">第一个参数是否是包含文件名称的全路径</param>
<returns>文件流</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.FileService.GetFileStreamResult(System.Byte[],System.String)">
<summary>
按字节数组转为文件流
</summary>
<param name="byteArray">字节数组</param>
<param name="fileName">文件名称</param>
<returns>文件流</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.FileService.UploadFileAsync(Microsoft.AspNetCore.Components.Forms.IBrowserFile,System.String)">
<summary>
上传文件,保存在磁盘中
</summary>
<param name="pPath">保存路径</param>
<param name="file">文件</param>
<returns>最终全路径</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.FileService.Verification(Microsoft.AspNetCore.Components.Forms.IBrowserFile,System.Int32,System.String[])">
<summary>
验证文件信息
</summary>
<param name="file">文件</param>
<param name="maxSize">最大文件大小</param>
<param name="allowTypes">扩展名称匹配</param>
</member>
<member name="M:ThingsGateway.Admin.Application.IFileService.GetFileStreamResult(System.String,System.String,System.Boolean)">
<summary>
获取本地存储文件流
</summary>
<param name="path">文件夹路径</param>
<param name="fileName">文件名称</param>
<param name="isPathFolder">路径是否包含文件名称</param>
<returns>文件流</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.IFileService.GetFileStreamResult(System.Byte[],System.String)">
<summary>
按字节数组转为文件流
</summary>
<param name="byteArray">字节数组</param>
<param name="fileName">文件名称</param>
<returns>文件流</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.IFileService.UploadFileAsync(Microsoft.AspNetCore.Components.Forms.IBrowserFile,System.String)">
<summary>
上传文件,保存在磁盘中
</summary>
<param name="pPath">保存路径</param>
<param name="file">文件流</param>
<returns>最终全路径</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.IFileService.Verification(Microsoft.AspNetCore.Components.Forms.IBrowserFile,System.Int32,System.String[])">
<summary>
验证文件信息
</summary>
<param name="file">文件流</param>
<param name="maxSize">最大文件大小单位MB</param>
<param name="allowTypes">允许上传的文件类型</param>
</member>
<member name="M:ThingsGateway.Admin.Application.IImportExportService.ExportAsync``1(System.Object,System.String,System.Boolean)">
<summary>
导出excel文件流
</summary>
<typeparam name="T">实体</typeparam>
<param name="input">实体对象或者IDataReader</param>
<param name="fileName">文件名称</param>
<param name="isDynamicExcelColumn">动态excel列根据实体的<see cref="T:ThingsGateway.Core.IgnoreExcelAttribute"/>属性判断是否生成 </param>
<returns>导出的文件流</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.IImportExportService.GetUrlEncodeFileName(System.String)">
<summary>
获取文件名默认xlsx类型
</summary>
<param name="fileName">文件名称不含类型名称的话默认xlsx</param>
<returns>编码后的文件名</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.IImportExportService.UploadFileAsync(Microsoft.AspNetCore.Components.Forms.IBrowserFile)">
<summary>
上传文件
</summary>
<param name="file">文件</param>
<returns>保存全路径</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.ImportExportService.ExportAsync``1(System.Object,System.String,System.Boolean)">
<summary>
导出excel文件流
</summary>
<typeparam name="T">实体</typeparam>
<param name="input">实体对象或者IDataReader</param>
<param name="fileName">文件名称</param>
<param name="isDynamicExcelColumn">动态excel列根据实体的<see cref="T:ThingsGateway.Core.IgnoreExcelAttribute"/>属性判断是否生成 </param>
<returns></returns>
</member>
<member name="M:ThingsGateway.Admin.Application.ImportExportService.UploadFileAsync(Microsoft.AspNetCore.Components.Forms.IBrowserFile)">
<summary>
上传文件
</summary>
<param name="file">文件</param>
<returns>保存全路径</returns>
</member>
<member name="M:ThingsGateway.Admin.Application.ImportExportService.GetUrlEncodeFileName(System.String)">
<summary>
获取文件名默认xlsx类型
</summary>
<param name="fileName">文件名称不含类型名称的话默认xlsx</param>
<returns></returns>
</member>
<member name="M:ApiPermissionService.ApiPermissionTreeSelector">
<inheritdoc />
</member>
</members>
</doc>

View File

@@ -13,7 +13,10 @@ using ThingsGateway.Admin.Application;
public interface IApiPermissionService
{
/// <inheritdoc />
/// <summary>
/// 获取API树
/// </summary>
/// <returns></returns>
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector();
}

View File

@@ -18,12 +18,26 @@ namespace ThingsGateway.Admin.Application;
public interface IAppService
{
/// <summary>
/// ClientInfo
/// </summary>
public ClientInfo? ClientInfo { get; }
/// <summary>
/// ClaimsPrincipal
/// </summary>
public ClaimsPrincipal? User { get; }
/// <summary>
/// RemoteIpAddress
/// </summary>
public IPAddress? RemoteIpAddress { get; }
/// <summary>
/// GetReturnUrl
/// </summary>
/// <param name="returnUrl"></param>
/// <returns></returns>
public string GetReturnUrl(string returnUrl);

View File

@@ -9,6 +9,7 @@
//------------------------------------------------------------------------------
using ThingsGateway.Admin.Application;
using ThingsGateway.Core;
namespace ThingsGateway.Admin.NetCore;

View File

@@ -41,32 +41,6 @@
"ThingsGateway.Admin.Razor.MainLayout": {
"About": "About",
"FullScreenButton": "Full Screen",
"UserCenter": "User Center",
"ChoiceModule": "Switch Module",
"GiteePostTitleText": "Code Commit Push Notification",
"LoginErrorh1": "Login Error",
"LoginSuccessh1": "Login Success",
"LoginSuccessc1": "Redirecting to the page",
"LoginErrorh2": "Login Failed",
"LoginErrorc2": "Please contact the administrator!",
"Logout": "Logout",
"系统首页": "Home",
"权限管理": "Permission",
"用户管理": "User",
"角色管理": "Role",
"菜单管理": "Menu",
"系统运维": "SystemOperation",
"系统配置": "System",
"字典管理": "Dictionary",
"操作日志": "Operation",
"会话管理": "Session",
"硬件信息": "HardwareInfo"
},
"ThingsGateway.Admin.Razor.OperLogPage": {
"Date": "Date",
"Count": "Count",

View File

@@ -42,32 +42,6 @@
},
"ThingsGateway.Admin.Razor.MainLayout": {
"About": "关于",
"FullScreenButton": "全屏",
"UserCenter": "个人中心",
"ChoiceModule": "切换模块",
"GiteePostTitleText": "代码提交推送通知",
"LoginErrorh1": "登录异常",
"LoginSuccessh1": "登录成功",
"LoginSuccessc1": "即将跳转页面",
"LoginErrorh2": "登录失败",
"LoginErrorc2": "请联系管理员!",
"Logout": "注销",
"系统首页": "系统首页",
"权限管理": "权限管理",
"用户管理": "用户管理",
"角色管理": "角色管理",
"菜单管理": "菜单管理",
"系统运维": "系统运维",
"系统配置": "系统配置",
"字典管理": "字典管理",
"操作日志": "操作日志",
"会话管理": "会话管理",
"硬件信息": "硬件信息"
},
"ThingsGateway.Admin.Razor.OperLogPage": {
"Date": "日期",
"Count": "数量",

View File

@@ -9,7 +9,7 @@
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\tools\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
<ProjectReference Include="$(SolutionDir)\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
<PackageReference Include="BootstrapBlazor.Chart" Version="8.1.9" />
<PackageReference Include="BootstrapBlazor.Chart" Version="8.2.0" />
<PackageReference Include="BootstrapBlazor.TableExport" Version="8.1.0" />
</ItemGroup>

View File

@@ -11,6 +11,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
using ThingsGateway.Admin.Application;
using ThingsGateway.Core;
@@ -19,13 +21,14 @@ namespace ThingsGateway.Gateway.Application;
/// <summary>
/// 采集设备
/// </summary>
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
[DisplayName("获取配置信息")]
[Route("openApi/configInfo")]
[RolePermission]
[LoggingMonitor]
[Authorize(AuthenticationSchemes = "Bearer")]
public class ConfigInfoControler : ControllerBase
{
/// <inheritdoc cref="ConfigInfoControler"/>
public ConfigInfoControler(
IChannelService channelService,
IVariableService variableService,
@@ -45,6 +48,7 @@ public class ConfigInfoControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpGet("channelList")]
[DisplayName("获取通道信息")]
public Task<SqlSugarPagedList<Channel>> GetChannelList([FromQuery] ChannelPageInput input)
{
return _channelService.PageAsync(input);
@@ -55,6 +59,7 @@ public class ConfigInfoControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpGet("deviceList")]
[DisplayName("获取设备信息")]
public Task<SqlSugarPagedList<Device>> GetCollectDeviceList([FromQuery] DevicePageInput input)
{
return _collectDeviceService.PageAsync(input);
@@ -65,6 +70,7 @@ public class ConfigInfoControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpGet("variableList")]
[DisplayName("获取变量信息")]
public Task<SqlSugarPagedList<Variable>> GetVariableList([FromQuery] VariablePageInput input)
{
return _variableService.PageAsync(input);

View File

@@ -11,7 +11,10 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
using ThingsGateway.Admin.Application;
using ThingsGateway.ASPNetCore;
using ThingsGateway.Foundation;
namespace ThingsGateway.Gateway.Application;
@@ -19,13 +22,13 @@ namespace ThingsGateway.Gateway.Application;
/// <summary>
/// 设备控制
/// </summary>
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
[Route("openApi/control")]
[RolePermission]
[LoggingMonitor]
[Authorize(AuthenticationSchemes = "Bearer")]
public class ControlControler : ControllerBase
{
/// <inheritdoc cref="ControlControler"/>
public ControlControler(IRpcService rpcService)
{
_rpcService = rpcService;
@@ -38,6 +41,7 @@ public class ControlControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpPost("pasueBusinessThread")]
[DisplayName("控制业务线程启停")]
public void PasueBusinessThread(long id, bool isStart)
{
HostedServiceUtil.BusinessDeviceHostedService.PasueThread(id, isStart);
@@ -48,6 +52,7 @@ public class ControlControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpPost("pasueCollectThread")]
[DisplayName("控制采集线程启停")]
public void PasueCollectThread(long id, bool isStart)
{
HostedServiceUtil.CollectDeviceHostedService.PasueThread(id, isStart);
@@ -58,6 +63,7 @@ public class ControlControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpPost("restartBusinessThread")]
[DisplayName("重启业务线程")]
public async Task RestartBusinessDeviceThread(long id)
{
if (id <= 0)
@@ -75,6 +81,7 @@ public class ControlControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpPost("restartCollectThread")]
[DisplayName("重启采集线程")]
public async Task RestartCollectDeviceThread(long id)
{
if (id <= 0)
@@ -91,6 +98,7 @@ public class ControlControler : ControllerBase
/// 写入多个变量
/// </summary>
[HttpPost("writeVariables")]
[DisplayName("写入变量")]
public async Task<Dictionary<string, OperResult>> WriteDeviceMethods(Dictionary<string, string> objs)
{
var result = await _rpcService.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", objs);

View File

@@ -19,6 +19,7 @@ namespace ThingsGateway.Gateway.Application;
/// <summary>
/// 导出文件
/// </summary>
[ApiDescriptionSettings(false)]
[Route("api/gatewayExport")]
[LoggingMonitor]
public class GatewayExportController : ControllerBase
@@ -31,9 +32,6 @@ public class GatewayExportController : ControllerBase
private readonly IImportExportService _importExportService;
/// <summary>
/// <inheritdoc cref="GatewayExportController"/>
/// </summary>
public GatewayExportController(
IChannelService channelService,
IDeviceService deviceService,

View File

@@ -15,6 +15,8 @@ using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using System.ComponentModel;
using ThingsGateway.Admin.Application;
using ThingsGateway.Core;
using ThingsGateway.NewLife.X.Extension;
@@ -23,6 +25,8 @@ namespace ThingsGateway.Gateway.Application;
/// <summary>
/// 采集状态信息
/// </summary>
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
[DisplayName("数据状态")]
[Route("openApi/runtimeInfo")]
[RolePermission]
[LoggingMonitor]
@@ -34,6 +38,7 @@ public class RuntimeInfoControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpGet("deviceList")]
[DisplayName("获取设备信息")]
public SqlSugarPagedList<DeviceData> GetCollectDeviceList([FromQuery] DevicePageInput input)
{
var data = GlobalData.ReadOnlyCollectDevices.Select(a => a.Value)
@@ -50,6 +55,7 @@ public class RuntimeInfoControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpGet("realAlarmList")]
[DisplayName("获取实时报警信息")]
public SqlSugarPagedList<VariableData> GetRealAlarmList([FromQuery] VariablePageInput input)
{
var data = GlobalData.ReadOnlyRealAlarmVariables
@@ -64,6 +70,7 @@ public class RuntimeInfoControler : ControllerBase
/// </summary>
/// <returns></returns>
[HttpGet("variableList")]
[DisplayName("获取变量信息")]
public SqlSugarPagedList<VariableData> GetVariableList([FromQuery] VariablePageInput input)
{
var data = GlobalData.ReadOnlyVariables.Select(a => a.Value)

View File

@@ -1,12 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="$(SolutionDir)PackNuget.props" />
<PropertyGroup>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<ProjectReference Include="$(SolutionDir)\ThingsGateway.Admin.ASPNetCore\ThingsGateway.Admin.ASPNetCore.csproj" />
</ItemGroup>

View File

@@ -27,46 +27,6 @@
"WriteVariable": "Write",
"WriteValue": "Value"
},
"ThingsGateway.Gateway.Razor.MainLayout": {
"About": "About",
"FullScreenButton": "Full Screen",
"UserCenter": "User Center",
"ChoiceModule": "Switch Module",
"GiteePostTitleText": "Code Commit Push Notification",
"LoginErrorh1": "Login Error",
"LoginSuccessh1": "Login Success",
"LoginSuccessc1": "Redirecting to the page",
"LoginErrorh2": "Login Failed",
"LoginErrorc2": "Please contact the administrator!",
"Logout": "Logout",
"系统首页": "Home",
"权限管理": "Permission",
"用户管理": "User",
"角色管理": "Role",
"菜单管理": "Menu",
"系统运维": "SystemOperation",
"系统配置": "System",
"字典管理": "Dictionary",
"操作日志": "Operation",
"会话管理": "Session",
"硬件信息": "HardwareInfo",
"网关管理": "GatewayManagement",
"插件管理": "PluginManagement",
"插件调试": "PluginDebugging",
"通道管理": "Channel",
"采集设备": "CollectionDevices",
"业务设备": "BusinessDevices",
"变量管理": "Variable",
"网关状态": "GatewayStatus",
"设备状态": "Device",
"实时数据": "RealTimeData",
"实时报警": "RealTimeAlarms",
"网关日志": "GatewayLogs",
"后台日志": "Backend",
"RPC日志": "RPC"
},
"ThingsGateway.Gateway.Razor.DriverDebugPage": {
"PluginUINotNull": "This plugin does not implement a debug page",
"New": "New"

View File

@@ -25,46 +25,6 @@
"WriteVariable": "写入",
"WriteValue": "写入值"
},
"ThingsGateway.Gateway.Razor.MainLayout": {
"About": "关于",
"FullScreenButton": "全屏",
"UserCenter": "个人中心",
"ChoiceModule": "切换模块",
"GiteePostTitleText": "代码提交推送通知",
"LoginErrorh1": "登录异常",
"LoginSuccessh1": "登录成功",
"LoginSuccessc1": "即将跳转页面",
"LoginErrorh2": "登录失败",
"LoginErrorc2": "请联系管理员!",
"Logout": "注销",
"系统首页": "系统首页",
"权限管理": "权限管理",
"用户管理": "用户管理",
"角色管理": "角色管理",
"菜单管理": "菜单管理",
"系统运维": "系统运维",
"系统配置": "系统配置",
"字典管理": "字典管理",
"操作日志": "操作日志",
"会话管理": "会话管理",
"硬件信息": "硬件信息",
"网关管理": "网关管理",
"插件管理": "插件管理",
"插件调试": "插件调试",
"通道管理": "通道管理",
"采集设备": "采集设备",
"业务设备": "业务设备",
"变量管理": "变量管理",
"网关状态": "网关状态",
"设备状态": "设备状态",
"实时数据": "实时数据",
"实时报警": "实时报警",
"网关日志": "网关日志",
"后台日志": "后台日志",
"RPC日志": "RPC日志"
},
"ThingsGateway.Gateway.Razor.DriverDebugPage": {
"PluginUINotNull": "此插件未实现调试页面",

View File

@@ -1,88 +0,0 @@
@inherits LayoutComponentBase
@layout BaseLayout
@namespace ThingsGateway.Gateway.Razor
@using ThingsGateway.Admin.Application
@using ThingsGateway.Admin.Razor
@using ThingsGateway.Core.Extension
@inject NavigationManager NavigationManager
<SysSignalR />
<CascadingValue Value="ReloadMenu" Name="ReloadMenu" IsFixed="true">
<CascadingValue Value="ReloadUser" Name="ReloadUser" IsFixed="true">
<div class="mainlayout">
<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true"
ShowGotoTop="true" ShowCollapseBar="true" Menus="@MenuService.MenuItems"
AdditionalAssemblies=NetCoreApp.RazorAssemblies AllowDragTab=true
UseTabSet="false" TabDefaultUrl="/">
<Header>
<div class="flex-fill"></div>
@* 搜索框 *@
<GlobalSearch Menus=@(MenuService.SameLevelMenuItems) />
@* 语言选择 *@
<div class="d-none d-xl-flex ">
<CultureChooser />
</div>
<Logout ImageUrl="@(AppContext.CurrentUser.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.UserAccount">
<LinkTemplate>
@* 切换模块 *@
<a @onclick="ChoiceModule"><i class="fas fa-arrow-right-arrow-left me-2" />@Localizer["ChoiceModule"]</a>
<a @onclick="@OnUserInfoDialog"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["UserCenter"]</a>
<a @onclick="@LogoutAsync"><i class="fa-solid fa-key me-2"></i>@Localizer["Logout"]</a>
</LinkTemplate>
</Logout>
@* 全屏按钮 *@
<FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt"
TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" />
@if (WebsiteOption.Value.IsShowAbout)
{
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
}
@* 版本号 *@
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div>
@* 主题切换 *@
@* <ThemeToggle /> *@
<ThemeProvider class="layout-header-bar d-none d-lg-flex px-0"></ThemeProvider>
</Header>
<Side>
<div class="layout-banner">
<span class="avatar">
@WebsiteOption.Value.Title?.GetNameLen2()
</span>
<div class="layout-title d-flex align-items-center justify-content-center">
<span>@WebsiteOption.Value.Title</span>
</div>
</div>
</Side>
<Main>
<Tab ClickTabToNavigation="true" ShowExtendButtons="true" ShowClose="true" AllowDrag=true
AdditionalAssemblies="@NetCoreApp.RazorAssemblies" Menus="@MenuService.MenuItems"
DefaultUrl=@("/") Body=@(Body!)>
</Tab>
</Main>
<NotAuthorized>
<Redirect Url="/Account/Login" />
</NotAuthorized>
</Layout>
@if (AppContext.IsHasButtonWithRole(string.Empty, "快捷按钮"))
{
<QuickActions></QuickActions>
}
</div>
</CascadingValue>
</CascadingValue>

View File

@@ -1,252 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.Extensions.Options;
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.Admin.Razor;
using ThingsGateway.Core;
using ThingsGateway.Razor;
namespace ThingsGateway.Gateway.Razor;
public partial class MainLayout : IDisposable
{
#region
[Inject]
[NotNull]
private IDispatchService<MessageItem>? DispatchService { get; set; }
private async Task Dispatch(DispatchEntry<MessageItem> entry)
{
if (entry.Entry != null)
{
await ToastService.Show(new ToastOption()
{
Title = $"{entry.Entry.Title} ",
Content = $"{entry.Entry.Message}",
Category = ToastCategory.Information,
Delay = 10 * 1000,
ForceDelay = true
});
}
}
#endregion
#region Gitee通知
[Inject]
[NotNull]
private IDispatchService<GiteePostBody>? CommitDispatchService { get; set; }
[NotNull]
private string? GiteePostTitleText { get; set; }
private async Task NotifyCommit(DispatchEntry<GiteePostBody> payload)
{
if (payload.CanDispatch())
{
var option = new ToastOption()
{
Category = ToastCategory.Information,
Title = GiteePostTitleText,
Delay = 10 * 1000,
ForceDelay = true,
ChildContent = BootstrapDynamicComponent.CreateComponent<CommitItem>(new Dictionary<string, object?>
{
[nameof(CommitItem.Item)] = payload.Entry
}).Render()
};
await ToastService.Show(option);
}
}
#endregion Gitee通知
#region
[Inject]
private ISysResourceService SysResourceService { get; set; }
[Inject]
private IUserCenterService UserCenterService { get; set; }
private async Task ChoiceModule()
{
DialogOption? op = null;
op = new DialogOption()
{
IsScrolling = true,
Size = Size.ExtraLarge,
ShowFooter = false,
Title = Localizer["ChoiceModule"],
BodyTemplate = BootstrapDynamicComponent.CreateComponent<ChoiceModuleComponent>(new Dictionary<string, object?>
{
[nameof(ChoiceModuleComponent.ModuleList)] = AppContext.CurrentUser.ModuleList.ToList(),
[nameof(ChoiceModuleComponent.Value)] = AppContext.CurrentUser.DefaultModule,
[nameof(ChoiceModuleComponent.OnSave)] = new Func<long, Task>(async v =>
{
if (op != null)
{
await op.CloseDialogAsync();
}
await UserCenterService.SetDefaultModule(v);
NavigationManager.NavigateTo("/", true);
})
}).Render(),
};
await DialogService.Show(op);
}
#endregion
#region
[Inject]
private AjaxService AjaxService { get; set; }
[Inject]
private IAppService AppService { get; set; }
[Inject]
[NotNull]
private IAuthRazorService? AuthRazorService { get; set; }
private async Task LogoutAsync()
{
try
{
var ret = await AuthRazorService.LoginOutAsync();
if (ret.Code != 200)
{
await ToastService.Error(Localizer["LoginErrorh1"], $"{ret.Msg}");
}
else
{
await ToastService.Information(Localizer["LoginSuccessh1"], Localizer["LoginSuccessc1"]);
await Task.Delay(1000);
var url = AppService.GetReturnUrl(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
await AjaxService.Goto(url);
}
}
catch
{
await ToastService.Error(Localizer["LoginErrorh2"], Localizer["LoginErrorc2"]);
}
}
#endregion
private string _versionString = string.Empty;
[Inject]
[NotNull]
private BlazorAppContext? AppContext { get; set; }
[Inject]
private DialogService DialogService { get; set; }
[Inject]
[NotNull]
private FullScreenService FullScreenService { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<MainLayout>? Localizer { get; set; }
[Inject]
[NotNull]
private IMenuService? MenuService { get; set; }
[Inject]
[NotNull]
private ToastService? ToastService { get; set; }
[Inject]
[NotNull]
private IAppVersionService? VersionService { get; set; }
[Inject]
[NotNull]
private IOptions<WebsiteOptions>? WebsiteOption { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected override async Task OnInitializedAsync()
{
GiteePostTitleText = Localizer[nameof(GiteePostTitleText)];
_versionString = $"v{VersionService.Version}";
DispatchService.Subscribe(Dispatch);
CommitDispatchService.Subscribe(NotifyCommit);
await AppContext.InitUserAsync();
await AppContext.InitMenus(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
await base.OnInitializedAsync();
}
private void Dispose(bool disposing)
{
if (disposing)
{
DispatchService.UnSubscribe(Dispatch);
CommitDispatchService.UnSubscribe(NotifyCommit);
}
}
private async Task ReloadMenu()
{
await AppContext.InitMenus(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
await InvokeAsync(StateHasChanged);
}
private async Task ReloadUser()
{
await AppContext.InitUserAsync();
await InvokeAsync(StateHasChanged);
}
private async Task ShowAbout()
{
DialogOption? op = null;
op = new DialogOption()
{
IsScrolling = true,
Size = Size.Medium,
ShowFooter = false,
Title = Localizer["About"],
BodyTemplate = BootstrapDynamicComponent.CreateComponent<About>().Render(),
};
await DialogService.Show(op);
}
#region
private Task OnUserInfoDialog() => DialogService.Show(new DialogOption()
{
Title = Localizer["UserCenter"],
ShowFooter = false,
Component = BootstrapDynamicComponent.CreateComponent<UserCenterPage>(new Dictionary<string, object?>()
{
})
});
#endregion
}

View File

@@ -1,138 +0,0 @@
::deep .avatar {
border-radius: 1.5rem;
width: 36px;
height: 36px;
background-color: var(--bs-green);
color: #fff;
flex: 0 0 auto;
font-size: 1rem;
}
.mainlayout ::deep .menu-icon {
width: 16px;
}
.mainlayout ::deep .layout-main > .tabs > .tabs-body {
background-color: var(--tabs-body-bg);
}
.mainlayout ::deep .layout-main > .tabs > .tabs-body > .tabs-body-content {
height: var(--bb-layout-body-height);
background-color: var(--bs-body-bg);
padding: 4px;
}
.mainlayout ::deep .tabs {
--bb-tabs-item-height: 32px;
--bb-tabs-body-padding: 0.5rem;
}
.mainlayout ::deep .tabs.tabs-border-card {
box-shadow: 0 0px 0px 0 rgba(0,0,0,0),0 0 6px 0 rgba(0,0,0,0);
}
.mainlayout ::deep .tabs .extend .nav-link-bar.left {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs-nav-wrap > .nav-link-bar.dropdown {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs .extend .nav-link-bar.right {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs .tabs-item-fix {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs.tabs-card > .tabs-header .tabs-item {
border-width: 0px 0px 0px 0px;
border: none;
}
.mainlayout ::deep .tabs.tabs-border-card > .tabs-header .tabs-item {
border-width: 0px 0px 0px 0px;
border: none;
}
.mainlayout ::deep .tabs.tabs-card .tabs-header .tabs-item.active {
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: var(--bb-tabs-item-active-color);
}
.mainlayout ::deep .tabs.tabs-border-card .tabs-header .tabs-item.active {
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: var(--bb-tabs-item-active-color);
}
.mainlayout ::deep .tabs.tabs-card .tabs-header .tabs-item.active {
background-color: var(--bs-primary-bg1);
}
.mainlayout ::deep.tabs.tabs-card .tabs-header .tabs-item:hover {
background-color: var(--bs-primary-bg1);
}
.mainlayout ::deep.tabs.tabs-border-card .tabs-header .tabs-item:hover {
background-color: var(--bs-primary-bg1);
}
.mainlayout ::deep .tabs-nav-wrap .nav-link-bar {
font-size: 0.7rem;
}
.mainlayout ::deep .tabs-item .tabs-item-close {
top: 5px;
}
.mainlayout ::deep .table-wrapper {
border-radius: unset;
}
.mainlayout ::deep .layout-side {
box-shadow: inset -1px 0 0px 0px var(--bs-border-color); /* 下内阴影 */
}
.mainlayout ::deep .layout-banner {
box-shadow: inset -1px 0 0px 0px var(--bs-border-color); /* 下内阴影 */
}
.mainlayout ::deep .layout {
--bb-layout-header-height: 44px;
--bb-layout-headerbar-background: transparent;
--bs-navbar-color: var(--bb-layout-header-color);
--bs-navbar-hover-color: var(--bs-primary);
--bb-layout-header-background: var(--tg-nav-bg);
--bb-layout-sidebar-background: var(--tg-nav-bg);
--bb-layout-footer-background: var(--tg-nav-bg);
--bb-layout-sidebar-banner-background: var(--tg-nav-bg);
--bb-layout-banner-font-size: 1.2rem;
--bb-layout-banner-logo-width: 36px;
--bb-layout-banner-logo-height: 36px;
--line-chart-height: 350px;
--bb-layout-body-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - 20px);
--line-chart-table-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - var(--line-chart-height) - 30px);
--table-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - 30px);
--bs-header-height: 30px;
}
.mainlayout ::deep .dropdown-logout {
--bb-logout-avatar-width: 32px;
--bb-logout-avatar-height: 32px;
--bb-logout-user-bg: rgba(52,58,64,0.7);
--bb-logout-menu-border-color: var(--bs-border-color);
}
.mainlayout ::deep .layout-header-bar {
border-color: transparent;
border: 0px;
color: var(--bb-layout-header-color);
}
.mainlayout ::deep .layout-header-bar:hover {
color: var(--bs-navbar-hover-color);
}

View File

@@ -63,9 +63,9 @@ internal class Program
app.MainWindow.GrantBrowserPermissions = true;
app.MainWindow.SetUseOsDefaultLocation(false);
app.MainWindow.SetUseOsDefaultSize(false);
app.MainWindow.SetSize(new System.Drawing.Size(1600, 900));
app.MainWindow.SetSize(new System.Drawing.Size(1920, 1080));
app.MainWindow.SetTitle("ThingsGateway");
app.MainWindow.SetIconFile("wwwroot/favicon.ico");
app.MainWindow.SetIconFile("favicon.ico");
AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
{
};

View File

@@ -5,17 +5,8 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="NetCoreApp.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)">
<Found Context="routeData">
@{
#if Admin
}
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Admin.Razor.MainLayout)" />
@{
#else
}
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Gateway.Razor.MainLayout)" />
@{
#endif
}
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Razor.MainLayout)" />
</Found>
<NotFound>

View File

@@ -43,12 +43,7 @@ public class Startup : AppStartup
var userCenterService = a.GetService<IUserCenterService>();
var userService = a.GetService<ISysUserService>();
var appContext = new BlazorAppContext(sysResourceService, userCenterService, userService);
#if Admin
appContext.TitleLocalizer = a.GetRequiredService<IStringLocalizer<ThingsGateway.Admin.Razor.MainLayout>>();
#else
appContext.TitleLocalizer = a.GetRequiredService<IStringLocalizer<ThingsGateway.Gateway.Razor.MainLayout>>();
#endif
appContext.TitleLocalizer = a.GetRequiredService<IStringLocalizer<ThingsGateway.Razor.MainLayout>>();
return appContext;
});
services.AddAuthorizationCore();

View File

@@ -59,7 +59,11 @@
<Content Include="..\ThingsGateway.Server\Index\GatewayIndex.razor" Link="Index\GatewayIndex.razor" />
<Compile Include="..\ThingsGateway.Server\Index\AdminIndex.razor.cs" Link="Index\AdminIndex.razor.cs" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndex.razor.cs" Link="Index\GatewayIndex.razor.cs" />
<Content Include="..\ThingsGateway.Server\MainLayout.razor" Link="MainLayout.razor" />
<Compile Include="..\ThingsGateway.Server\MainLayout.razor.cs" Link="MainLayout.razor.cs" />
<Content Include="..\ThingsGateway.Server\MainLayout.razor.css" Link="MainLayout.razor.css" />
<Content Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor" Link="Index\GatewayIndexComponent.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor.cs" Link="Index\GatewayIndexComponent.razor.cs" />
<None Include="..\ThingsGateway.Server\Index\AdminIndex.razor.css" Link="Index\AdminIndex.razor.css" />
<None Include="..\ThingsGateway.Server\Index\GatewayIndex.razor.css" Link="Index\GatewayIndex.razor.css" />
@@ -72,7 +76,7 @@
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
@@ -84,6 +88,11 @@
</ItemGroup>
<ItemGroup>
<None Remove="favicon.ico" />
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Update="pm2-windows.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -1,127 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace ThingsGateway.Server;
/// <summary>
/// 安全定义需求子项
/// </summary>
public sealed class SpecificationOpenApiSecurityRequirementItem
{
/// <summary>
/// 构造函数
/// </summary>
public SpecificationOpenApiSecurityRequirementItem()
{
Accesses = System.Array.Empty<string>();
}
/// <summary>
/// 权限
/// </summary>
public string[] Accesses { get; set; }
/// <summary>
/// 安全Schema
/// </summary>
public OpenApiSecurityScheme Scheme { get; set; }
}
/// <summary>
/// 规范化文档安全配置
/// </summary>
public sealed class SpecificationOpenApiSecurityScheme : OpenApiSecurityScheme
{
/// <summary>
/// 构造函数
/// </summary>
public SpecificationOpenApiSecurityScheme()
{
}
/// <summary>
/// 唯一Id
/// </summary>
public string Id { get; set; }
/// <summary>
/// 安全需求
/// </summary>
public SpecificationOpenApiSecurityRequirementItem Requirement { get; set; }
}
/// <inheritdoc/>
internal static class SwaggerExtensions
{
/// <summary>
/// 配置JWT授权
/// </summary>
/// <param name="swaggerGenOptions">Swagger 生成器配置</param>
internal static void ConfigureSecurities(this SwaggerGenOptions swaggerGenOptions)
{
var openApiSecurityRequirement = new OpenApiSecurityRequirement();
var SecurityDefinitions = new SpecificationOpenApiSecurityScheme[]
{
new SpecificationOpenApiSecurityScheme
{
Id="Bearer",
Type= SecuritySchemeType.Http,
Name="Authorization",
Description="JWT Authorization header using the Bearer scheme.",
BearerFormat="JWT",
Scheme="bearer",
In= ParameterLocation.Header,
Requirement=new SpecificationOpenApiSecurityRequirementItem
{
Scheme=new OpenApiSecurityScheme
{
Reference=new OpenApiReference
{
Id="Bearer",
Type= ReferenceType.SecurityScheme
}
},
Accesses=Array.Empty<string>()
}
}
};
// 生成安全定义
foreach (var securityDefinition in SecurityDefinitions)
{
// Id 必须定义
if (string.IsNullOrWhiteSpace(securityDefinition.Id)
|| swaggerGenOptions.SwaggerGeneratorOptions.SecuritySchemes.ContainsKey(securityDefinition.Id)) continue;
// 添加安全定义
var openApiSecurityScheme = securityDefinition as OpenApiSecurityScheme;
swaggerGenOptions.AddSecurityDefinition(securityDefinition.Id, openApiSecurityScheme);
// 添加安全需求
var securityRequirement = securityDefinition.Requirement;
// C# 9.0 模式匹配新语法
if (securityRequirement is { Scheme.Reference: not null })
{
securityRequirement.Scheme.Reference.Id ??= securityDefinition.Id;
openApiSecurityRequirement.Add(securityRequirement.Scheme, securityRequirement.Accesses);
}
}
// 添加安全需求
if (openApiSecurityRequirement.Count > 0)
{
swaggerGenOptions.AddSecurityRequirement(openApiSecurityRequirement);
}
}
}

View File

@@ -1,4 +1,43 @@
{
"ThingsGateway.Razor.MainLayout": {
"About": "About",
"FullScreenButton": "Full Screen",
"UserCenter": "User Center",
"ChoiceModule": "Switch Module",
"LoginErrorh1": "Login Error",
"LoginSuccessh1": "Login Success",
"LoginSuccessc1": "Redirecting to the page",
"LoginErrorh2": "Login Failed",
"LoginErrorc2": "Please contact the administrator!",
"Logout": "Logout",
"系统首页": "Home",
"权限管理": "Permission",
"用户管理": "User",
"角色管理": "Role",
"菜单管理": "Menu",
"系统运维": "SystemOperation",
"系统配置": "System",
"字典管理": "Dictionary",
"操作日志": "Operation",
"会话管理": "Session",
"硬件信息": "HardwareInfo",
"网关管理": "GatewayManagement",
"插件管理": "PluginManagement",
"插件调试": "PluginDebugging",
"通道管理": "Channel",
"采集设备": "CollectionDevices",
"业务设备": "BusinessDevices",
"变量管理": "Variable",
"网关状态": "GatewayStatus",
"设备状态": "Device",
"实时数据": "RealTimeData",
"实时报警": "RealTimeAlarms",
"网关日志": "GatewayLogs",
"后台日志": "Backend",
"RPC日志": "RPC"
},
"ThingsGateway.Admin.Razor.AdminIndex": {
"CollectDevice": "Collect Device",

View File

@@ -1,4 +1,45 @@
{
"ThingsGateway.Razor.MainLayout": {
"About": "关于",
"FullScreenButton": "全屏",
"UserCenter": "个人中心",
"ChoiceModule": "切换模块",
"LoginErrorh1": "登录异常",
"LoginSuccessh1": "登录成功",
"LoginSuccessc1": "即将跳转页面",
"LoginErrorh2": "登录失败",
"LoginErrorc2": "请联系管理员!",
"Logout": "注销",
"系统首页": "系统首页",
"权限管理": "权限管理",
"用户管理": "用户管理",
"角色管理": "角色管理",
"菜单管理": "菜单管理",
"系统运维": "系统运维",
"系统配置": "系统配置",
"字典管理": "字典管理",
"操作日志": "操作日志",
"会话管理": "会话管理",
"硬件信息": "硬件信息",
"网关管理": "网关管理",
"插件管理": "插件管理",
"插件调试": "插件调试",
"通道管理": "通道管理",
"采集设备": "采集设备",
"业务设备": "业务设备",
"变量管理": "变量管理",
"网关状态": "网关状态",
"设备状态": "设备状态",
"实时数据": "实时数据",
"实时报警": "实时报警",
"网关日志": "网关日志",
"后台日志": "后台日志",
"RPC日志": "RPC日志"
},
"ThingsGateway.Admin.Razor.AdminIndex": {
"CollectDevice": "采集设备",
"BusinessDevice": "业务设备",

View File

@@ -1,8 +1,25 @@
@inherits LayoutComponentBase
@layout BaseLayout
@namespace ThingsGateway.Admin.Razor
@namespace ThingsGateway.Razor
@using BootstrapBlazor.Components
@using ThingsGateway.Admin.Application
@using ThingsGateway.Admin.Razor
@using ThingsGateway.Core.Extension
@using ThingsGateway.Razor
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.Extensions.Localization
@using System.ComponentModel
@using System.ComponentModel.DataAnnotations
@inject NavigationManager NavigationManager
<SysSignalR />
@@ -47,6 +64,7 @@
@* 全屏按钮 *@
<FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt"
TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" />
@if (WebsiteOption.Value.IsShowAbout)
{
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
@@ -82,6 +100,18 @@
</NotAuthorized>
</Layout>
@{
#if !Admin
{
if (AppContext.IsHasButtonWithRole(string.Empty, "快捷按钮"))
{
<ThingsGateway.Gateway.Razor.QuickActions></ThingsGateway.Gateway.Razor.QuickActions>
}
}
#endif
}
</div>
</CascadingValue>

View File

@@ -8,10 +8,19 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;
using ThingsGateway.Admin.Application;
using ThingsGateway.Admin.Razor;
using ThingsGateway.Core;
namespace ThingsGateway.Admin.Razor;
namespace ThingsGateway.Razor;
public partial class MainLayout : IDisposable
{
@@ -29,7 +38,7 @@ public partial class MainLayout : IDisposable
{
Title = $"{entry.Entry.Title} ",
Content = $"{entry.Entry.Message}",
Category = ToastCategory.Information,
Category = entry.Entry.Category,
Delay = 10 * 1000,
ForceDelay = true
});
@@ -38,36 +47,6 @@ public partial class MainLayout : IDisposable
#endregion
#region Gitee通知
[Inject]
[NotNull]
private IDispatchService<GiteePostBody>? CommitDispatchService { get; set; }
[NotNull]
private string? GiteePostTitleText { get; set; }
private async Task NotifyCommit(DispatchEntry<GiteePostBody> payload)
{
if (payload.CanDispatch())
{
var option = new ToastOption()
{
Category = ToastCategory.Information,
Title = GiteePostTitleText,
Delay = 10 * 1000,
ForceDelay = true,
ChildContent = BootstrapDynamicComponent.CreateComponent<CommitItem>(new Dictionary<string, object?>
{
[nameof(CommitItem.Item)] = payload.Entry
}).Render()
};
await ToastService.Show(option);
}
}
#endregion Gitee通知
#region
[Inject]
@@ -108,14 +87,17 @@ public partial class MainLayout : IDisposable
#region
private Task OnUserInfoDialog() => DialogService.Show(new DialogOption()
private Task OnUserInfoDialog()
{
Title = Localizer["UserCenter"],
ShowFooter = false,
Component = BootstrapDynamicComponent.CreateComponent<UserCenterPage>(new Dictionary<string, object?>()
return DialogService.Show(new DialogOption()
{
})
});
Title = Localizer["UserCenter"],
ShowFooter = false,
Component = BootstrapDynamicComponent.CreateComponent<UserCenterPage>(new Dictionary<string, object?>()
{
})
});
}
#endregion
@@ -197,10 +179,8 @@ public partial class MainLayout : IDisposable
protected override async Task OnInitializedAsync()
{
GiteePostTitleText = Localizer[nameof(GiteePostTitleText)];
_versionString = $"v{VersionService.Version}";
DispatchService.Subscribe(Dispatch);
CommitDispatchService.Subscribe(NotifyCommit);
await AppContext.InitUserAsync();
await AppContext.InitMenus(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
await base.OnInitializedAsync();
@@ -211,7 +191,6 @@ public partial class MainLayout : IDisposable
if (disposing)
{
DispatchService.UnSubscribe(Dispatch);
CommitDispatchService.UnSubscribe(NotifyCommit);
}
}

View File

@@ -19,7 +19,9 @@ using System.Text;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using ThingsGateway.Admin.Application;
using ThingsGateway.ASPNetCore;
using Console = System.Console;
namespace ThingsGateway.Server;
@@ -105,9 +107,7 @@ public class Program
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(a => a.ConfigureSecurities());
builder.Services.AddSpecificationDocuments();
builder.Services
@@ -147,6 +147,7 @@ public class Program
var app = builder.Build();
app.UseServices();
app.UseSpecificationDocuments();
#region build
@@ -168,11 +169,7 @@ public class Program
app.UseResponseCompression();
app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = ctx => ctx.ProcessCache(app.Configuration) });
}
else
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseStaticFiles(new StaticFileOptions
{

View File

@@ -3,17 +3,9 @@
<Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="NetCoreApp.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)">
<Found Context="routeData">
@{
#if Admin
}
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Admin.Razor.MainLayout)" />
@{
#else
}
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Gateway.Razor.MainLayout)" />
@{
#endif
}
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Razor.MainLayout)" />
</Found>
<NotFound>

View File

@@ -9,8 +9,8 @@
//------------------------------------------------------------------------------
#if !Admin
using CSScriptLib;
using ThingsGateway.Gateway.Application;
using ThingsGateway.Gateway.Application;
#endif
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
@@ -23,6 +23,8 @@ using ThingsGateway.Logging;
using UAParser;
using ThingsGateway.ASPNetCore;
namespace ThingsGateway.Server;
[AppStartup(-99999)]
@@ -124,11 +126,7 @@ public class Startup : AppStartup
var userCenterService = a.GetService<IUserCenterService>();
var userService = a.GetService<ISysUserService>();
var appContext = new BlazorAppContext(sysResourceService, userCenterService, userService);
#if Admin
appContext.TitleLocalizer = a.GetRequiredService<IStringLocalizer<ThingsGateway.Admin.Razor.MainLayout>>();
#else
appContext.TitleLocalizer = a.GetRequiredService<IStringLocalizer<ThingsGateway.Gateway.Razor.MainLayout>>();
#endif
appContext.TitleLocalizer = a.GetRequiredService<IStringLocalizer<ThingsGateway.Razor.MainLayout>>();
return appContext;
});

View File

@@ -19,13 +19,6 @@
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<!--<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
  <AllowedReferenceRelatedFileExtensions>
    --><!-- 阻止默认的 XML 和 PDB 文件复制到 RELEASE 的输出目录. 只有*.allowedextension 扩展名的文件可以被包含, 当然这个扩展的文件并不存在.--><!--
    .allowedextension
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>-->
<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Admin'">
<DefineConstants>Admin</DefineConstants>
</PropertyGroup>
@@ -69,7 +62,6 @@
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
</ItemGroup>
@@ -81,6 +73,10 @@
</ItemGroup>
<ItemGroup>
<None Remove="favicon.ico" />
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Update="Dockerfile">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -12,6 +12,34 @@
"SignalRSupport": true
},
//swagger设置
"SpecificationDocumentSettings": {
"DocumentTitle": "ThingsGateway",
"DocExpansionState": "None",
"GroupOpenApiInfos": [
{
"Group": "ThingsGateway.OpenApi", //分组唯一标识string 类型,必填
"Order": 99, //分组排序int 类型,数字越大排前面,默认 0
"Title": "ThingsGateway物联网关接口", //配置分组标题string 类型
"Description": "物联网关Swagger",
"Version": "1.0.0", //配置分组版本,默认 1.0
"TermsOfService": "https://gitee.com/diego2098/ThingsGateway", //配置相关链接地址Uri 类型
"Contact": {
//配置联系方式
"Name": "Diego",
"Url": "https://gitee.com/diego2098/ThingsGateway",
"Email": "2248356998@qq.com"
},
"License": {
//配置协议OpenApiLicense 类型
"Name": "Apache-2.0",
"Url": "https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.txt"
}
}
],
"EnableEnumSchemaFilter": true
},
//冗余配置
"ManagementOptions": {

View File

@@ -9,6 +9,34 @@
"SignalRSupport": true
},
//swagger设置
"SpecificationDocumentSettings": {
"DocumentTitle": "ThingsGateway",
"DocExpansionState": "None",
"GroupOpenApiInfos": [
{
"Group": "ThingsGateway.OpenApi", //分组唯一标识string 类型,必填
"Order": 99, //分组排序int 类型,数字越大排前面,默认 0
"Title": "ThingsGateway物联网关接口", //配置分组标题string 类型
"Description": "物联网关Swagger",
"Version": "1.0.0", //配置分组版本,默认 1.0
"TermsOfService": "https://gitee.com/diego2098/ThingsGateway", //配置相关链接地址Uri 类型
"Contact": {
//配置联系方式
"Name": "Diego",
"Url": "https://gitee.com/diego2098/ThingsGateway",
"Email": "2248356998@qq.com"
},
"License": {
//配置协议OpenApiLicense 类型
"Name": "Apache-2.0",
"Url": "https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.txt"
}
}
],
"EnableEnumSchemaFilter": true
},
//冗余配置
"ManagementOptions": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>6.0.5.7</Version>
<Version>6.0.5.9</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -6,9 +6,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ThingsGateway.Foundation.Variable" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="*" />
<ProjectReference Include="..\ThingsGateway.Foundation.Modbus\ThingsGateway.Foundation.Modbus.csproj" />
<ProjectReference Include="..\ThingsGateway.Foundation.SiemensS7\ThingsGateway.Foundation.SiemensS7.csproj" />
<PackageReference Include="ThingsGateway.Foundation.Variable" Version="8.8.7" />
</ItemGroup>

View File

@@ -24,37 +24,7 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage
public override int HeaderLength => 7;
public ModbusRequest Request { get; set; } = new();
//主站发送的报文最低也有8个字节
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
var pos = byteBlock.Position - HeaderLength;
var crcLen = 0;
Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength);
if (Request.FunctionCode == 15)
{
Request.Data = byteBlock.AsSegmentTake(Request.Length).AsSpan().ByteToBoolArray(Request.Length).Select(a => a ? (byte)0xff : (byte)0).ToArray();
}
else if (Request.FunctionCode == 16)
{
Request.Data = byteBlock.AsSegmentTake(Request.Length);
}
crcLen = HeaderLength + BodyLength - 2;
var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen));
//Crc
var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2).ToArray();
if (crc.SequenceEqual(checkCrc))
{
OperCode = 0;
return FilterResult.Success;
}
return FilterResult.GoOn;
}
/// <inheritdoc/>
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
@@ -100,4 +70,36 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage
}
return false;
}
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
var pos = byteBlock.Position - HeaderLength;
var crcLen = 0;
Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength);
if (Request.FunctionCode == 15)
{
Request.Data = byteBlock.AsSegmentTake(Request.Length).AsSpan().ByteToBoolArray(Request.Length).Select(a => a ? (byte)0xff : (byte)0).ToArray();
}
else if (Request.FunctionCode == 16)
{
Request.Data = byteBlock.AsSegmentTake(Request.Length);
}
crcLen = HeaderLength + BodyLength - 2;
var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen));
//Crc
var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2).ToArray();
if (crc.SequenceEqual(checkCrc))
{
OperCode = 0;
return FilterResult.Success;
}
return FilterResult.GoOn;
}
}

View File

@@ -25,27 +25,6 @@ public class ModbusTcpSlaveMessage : MessageBase, IResultMessage
public ModbusRequest Request { get; set; } = new();
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
var pos = byteBlock.Position - HeaderLength;
Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength);
if (Request.FunctionCode == 15)
{
byteBlock.Position += 1;
Request.Data = byteBlock.AsSegmentTake(Request.Length).AsSpan().ByteToBoolArray(Request.Length).Select(a => a ? (byte)0xff : (byte)0).ToArray();
}
else if (Request.FunctionCode == 16)
{
byteBlock.Position += 1;
Request.Data = byteBlock.AsSegmentTake(Request.Length);
}
OperCode = 0;
return FilterResult.Success;
}
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
{
Sign = byteBlock.ReadUInt16(EndianType.Big);
@@ -88,4 +67,27 @@ public class ModbusTcpSlaveMessage : MessageBase, IResultMessage
}
return false;
}
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
var pos = byteBlock.Position - HeaderLength;
Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength);
if (Request.FunctionCode == 15)
{
byteBlock.Position += 1;
Request.Data = byteBlock.AsSegmentTake(Request.Length).AsSpan().ByteToBoolArray(Request.Length).Select(a => a ? (byte)0xff : (byte)0).ToArray();
}
else if (Request.FunctionCode == 16)
{
byteBlock.Position += 1;
Request.Data = byteBlock.AsSegmentTake(Request.Length);
}
OperCode = 0;
return FilterResult.Success;
}
}

View File

@@ -33,12 +33,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ThingsGateway.Foundation.Razor" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.OpcUa" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.OpcDa" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Dlt645" Version="*" />
<ProjectReference Include="..\..\adapter\ThingsGateway.Foundation.Dlt645\ThingsGateway.Foundation.Dlt645.csproj" />
<ProjectReference Include="..\..\adapter\ThingsGateway.Foundation.Modbus\ThingsGateway.Foundation.Modbus.csproj" />
<ProjectReference Include="..\..\adapter\ThingsGateway.Foundation.OpcDa\ThingsGateway.Foundation.OpcDa.csproj" />
<ProjectReference Include="..\..\adapter\ThingsGateway.Foundation.OpcUa\ThingsGateway.Foundation.OpcUa.csproj" />
<ProjectReference Include="..\..\adapter\ThingsGateway.Foundation.SiemensS7\ThingsGateway.Foundation.SiemensS7.csproj" />
<ProjectReference Include="..\..\tools\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj" />
</ItemGroup>

View File

@@ -14,7 +14,7 @@ using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
using System.Security.Claims;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// App静态类

View File

@@ -13,7 +13,7 @@ using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Net.Http.Headers;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <inheritdoc/>
public static class CacheExtensions

View File

@@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using ThingsGateway.Admin.Application;
using ThingsGateway.ASPNetCore;
namespace Microsoft.AspNetCore.Builder;

View File

@@ -14,7 +14,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using ThingsGateway;
using ThingsGateway.Admin.Application;
using ThingsGateway.ASPNetCore;
namespace Microsoft.Extensions.DependencyInjection;

View File

@@ -9,14 +9,107 @@
// ------------------------------------------------------------------------------
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Mvc;
namespace ThingsGateway.Admin.Application;
using System.Collections.Concurrent;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 常量、公共方法配置类
/// </summary>
internal static class Penetrates
{
/// <summary>
/// 分组分隔符
/// </summary>
internal const string GroupSeparator = "##";
/// <summary>
/// 请求动词映射字典
/// </summary>
internal static ConcurrentDictionary<string, string> VerbToHttpMethods { get; private set; }
/// <summary>
/// 控制器排序集合
/// </summary>
internal static ConcurrentDictionary<string, (string, int, Type)> ControllerOrderCollection { get; set; }
/// <summary>
/// 构造函数
/// </summary>
static Penetrates()
{
ControllerOrderCollection = new ConcurrentDictionary<string, (string, int, Type)>();
VerbToHttpMethods = new ConcurrentDictionary<string, string>
{
["post"] = "POST",
["add"] = "POST",
["create"] = "POST",
["insert"] = "POST",
["submit"] = "POST",
["get"] = "GET",
["find"] = "GET",
["fetch"] = "GET",
["query"] = "GET",
//["getlist"] = "GET",
//["getall"] = "GET",
["put"] = "PUT",
["update"] = "PUT",
["delete"] = "DELETE",
["remove"] = "DELETE",
["clear"] = "DELETE",
["patch"] = "PATCH"
};
IsApiControllerCached = new ConcurrentDictionary<Type, bool>();
}
/// <summary>
/// <see cref="IsApiController(Type)"/> 缓存集合
/// </summary>
private static readonly ConcurrentDictionary<Type, bool> IsApiControllerCached;
/// <summary>
/// 是否是Api控制器
/// </summary>
/// <param name="type">type</param>
/// <returns></returns>
internal static bool IsApiController(Type type)
{
return IsApiControllerCached.GetOrAdd(type, Function);
// 本地静态方法
static bool Function(Type type)
{
// 排除 OData 控制器
if (type.Assembly.GetName().Name.StartsWith("Microsoft.AspNetCore.OData")) return false;
// 不能是非公开、基元类型、值类型、抽象类、接口、泛型类
if (!type.IsPublic || type.IsPrimitive || type.IsValueType || type.IsAbstract || type.IsInterface || type.IsGenericType) return false;
// 继承 ControllerBase
if ((!typeof(Controller).IsAssignableFrom(type) && typeof(ControllerBase).IsAssignableFrom(type))
// 支持没有继承 ControllerBase 且贴了 [Route] 特性的情况
|| (type.IsDefined(typeof(RouteAttribute), true)))
{
// 处理运行时动态生成程序集问题
if (type.Assembly?.ManifestModule?.Name == "<Unknown>") return true;
// 解决 ASP.NET Core 启动时自动载入 NuGet 包导致模块化配置 SupportPackageNamePrefixs 出现非预期的结果
if (!App.EffectiveTypes.Any(t => t == type)) return false;
return true;
}
return false;
}
}
/// <summary>
/// 默认跨域导出响应头 Key
/// </summary>

View File

@@ -12,7 +12,7 @@ using Microsoft.Extensions.Configuration;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 跨域配置选项

View File

@@ -0,0 +1,86 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
// ------------------------------------------------------------------------------
namespace Microsoft.Extensions.Configuration;
/// <summary>
/// IConfiguration 接口拓展
/// </summary>
public static class IConfigurationExtensions
{
/// <summary>
/// 判断配置节点是否存在
/// </summary>
/// <param name="configuration">配置对象</param>
/// <param name="key">节点路径</param>
/// <returns>是否存在</returns>
public static bool Exists(this IConfiguration configuration, string key)
{
return configuration.GetSection(key).Exists();
}
/// <summary>
/// 获取配置节点并转换成指定类型
/// </summary>
/// <typeparam name="T">节点类型</typeparam>
/// <param name="configuration">配置对象</param>
/// <param name="key">节点路径</param>
/// <returns>节点类型实例</returns>
public static T Get<T>(this IConfiguration configuration, string key)
{
return configuration.GetSection(key).Get<T>();
}
/// <summary>
/// 获取配置节点并转换成指定类型
/// </summary>
/// <typeparam name="T">节点类型</typeparam>
/// <param name="configuration">配置对象</param>
/// <param name="key">节点路径</param>
/// <param name="configureOptions">配置值绑定到指定类型额外配置</param>
/// <returns>节点类型实例</returns>
public static T Get<T>(this IConfiguration configuration
, string key
, Action<BinderOptions> configureOptions)
{
return configuration.GetSection(key).Get<T>(configureOptions);
}
/// <summary>
/// 获取节点配置
/// </summary>
/// <param name="configuration">配置对象</param>
/// <param name="key">节点路径</param>
/// <param name="type">节点类型</param>
/// <returns><see cref="object"/> 实例</returns>
public static object Get(this IConfiguration configuration
, string key
, Type type)
{
return configuration.GetSection(key).Get(type);
}
/// <summary>
/// 获取节点配置
/// </summary>
/// <param name="configuration">配置对象</param>
/// <param name="key">节点路径</param>
/// <param name="type">节点类型</param>
/// <param name="configureOptions">配置值绑定到指定类型额外配置</param>
/// <returns><see cref="object"/> 实例</returns>
public static object Get(this IConfiguration configuration
, string key
, Type type
, Action<BinderOptions> configureOptions)
{
return configuration.GetSection(key).Get(type, configureOptions);
}
}

View File

@@ -13,15 +13,6 @@ using Microsoft.Extensions.DependencyInjection;
using ThingsGateway;
using ThingsGateway.Logging;
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
// ------------------------------------------------------------------------------
public static class LoggingServiceCollectionExtensions
{

View File

@@ -27,7 +27,7 @@ using System.Text.Json;
using ThingsGateway.Core.Json.Extension;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 规范化RESTful风格返回值

View File

@@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 规范化结果提供器

View File

@@ -18,7 +18,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Reflection;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 规范化结果上下文

View File

@@ -14,9 +14,10 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Localization;
using ThingsGateway.Core;
using ThingsGateway.NewLife.X;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 规范化RESTful风格返回值

View File

@@ -27,7 +27,7 @@ using System.Text.Json;
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// JWT 加解密

View File

@@ -8,7 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 日志常量

View File

@@ -8,7 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
/// <summary>
/// 认证信息

View File

@@ -34,7 +34,7 @@ using System.Text;
using System.Text.Json;
using ThingsGateway;
using ThingsGateway.Admin.Application;
using ThingsGateway.ASPNetCore;
using ThingsGateway.Core.Extension;
using ThingsGateway.Logging;
using ThingsGateway.NewLife.X;

View File

@@ -11,7 +11,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
namespace ThingsGateway.Admin.Application;
namespace ThingsGateway.ASPNetCore;
public abstract class AppAuthorizeHandler : IAuthorizationHandler
{

File diff suppressed because one or more lines are too long

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