Merge branch 'refs/heads/dev' into dev-blazor

# Conflicts:
#	src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj
This commit is contained in:
Argo Window10 2019-11-17 13:24:19 +08:00
commit bda547f321
167 changed files with 1671 additions and 1198 deletions

View File

@ -33,7 +33,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MongoDB", "MongoDB", "{A06A
db\MongoDB\Dicts.js = db\MongoDB\Dicts.js db\MongoDB\Dicts.js = db\MongoDB\Dicts.js
db\MongoDB\Groups.js = db\MongoDB\Groups.js db\MongoDB\Groups.js = db\MongoDB\Groups.js
db\MongoDB\init.js = db\MongoDB\init.js db\MongoDB\init.js = db\MongoDB\init.js
db\MongoDB\install.cmd = db\MongoDB\install.cmd db\MongoDB\install.ps1 = db\MongoDB\install.ps1
db\MongoDB\install.sh = db\MongoDB\install.sh db\MongoDB\install.sh = db\MongoDB\install.sh
db\MongoDB\Navigations.js = db\MongoDB\Navigations.js db\MongoDB\Navigations.js = db\MongoDB\Navigations.js
db\MongoDB\Roles.js = db\MongoDB\Roles.js db\MongoDB\Roles.js = db\MongoDB\Roles.js
@ -43,6 +43,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MySQL", "MySQL", "{084E2E94-6B7D-4D3E-9BF1-6972427FBF80}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MySQL", "MySQL", "{084E2E94-6B7D-4D3E-9BF1-6972427FBF80}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
db\MySQL\initData.sql = db\MySQL\initData.sql db\MySQL\initData.sql = db\MySQL\initData.sql
db\MySQL\install.ps1 = db\MySQL\install.ps1
db\MySQL\install.sh = db\MySQL\install.sh db\MySQL\install.sh = db\MySQL\install.sh
db\MySQL\install.sql = db\MySQL\install.sql db\MySQL\install.sql = db\MySQL\install.sql
db\MySQL\my.ini = db\MySQL\my.ini db\MySQL\my.ini = db\MySQL\my.ini

View File

@ -7,9 +7,14 @@
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)src\Keys\Longbow.Utility.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)src\Keys\Longbow.Utility.snk</AssemblyOriginatorKeyFile>
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<Target Condition=" $(IsWebProject) == true Or $(IsTestProject) == true" Name="PostBuild" AfterTargets="PostBuildEvent">
<Message Text="Copy lic file -> $(TargetDir)" Importance="high" />
<Copy Condition="'$(OS)' == 'Windows_NT'" DestinationFolder="$(TargetDir)" SourceFiles="$(MSBuildThisFileDirectory)src\admin\keys\Longbow.lic" SkipUnchangedFiles="true" />
<Copy Condition="'$(OS)' == 'UNIX'" DestinationFolder="$(TargetDir)" SourceFiles="$(MSBuildThisFileDirectory)src/admin/keys/Longbow.lic" SkipUnchangedFiles="true" />
</Target>
<Target Condition=" $(IsWebProject) == true " Name="PostPublish" AfterTargets="Publish"> <Target Condition=" $(IsWebProject) == true " Name="PostPublish" AfterTargets="Publish">
<Message Text="Publish lic file -> $(PublishDir)" Importance="high" /> <Message Text="Publish lic file -> $(PublishDir)" Importance="high" />
<Copy Condition="'$(OS)' == 'Windows_NT'" DestinationFolder="$(PublishDir)" SourceFiles="$(MSBuildThisFileDirectory)src\admin\keys\Longbow.lic" SkipUnchangedFiles="true" /> <Copy Condition="'$(OS)' == 'Windows_NT'" DestinationFolder="$(PublishDir)" SourceFiles="$(MSBuildThisFileDirectory)src\admin\keys\Longbow.lic" SkipUnchangedFiles="true" />
@ -19,11 +24,4 @@
<Copy Condition="'$(OS)' == 'UNIX'" DestinationFolder="$(PublishDir)" SourceFiles="$(MSBuildThisFileDirectory)src/admin/Bootstrap.Admin/BootstrapAdmin.db" SkipUnchangedFiles="true" /> <Copy Condition="'$(OS)' == 'UNIX'" DestinationFolder="$(PublishDir)" SourceFiles="$(MSBuildThisFileDirectory)src/admin/Bootstrap.Admin/BootstrapAdmin.db" SkipUnchangedFiles="true" />
</Target> </Target>
<Target Condition=" $(IsWebProject) == true " Name="PostBuild" AfterTargets="PostBuildEvent">
<Message Text="Copy lic file -> $(TargetDir)" Importance="high" />
<Copy Condition="'$(OS)' == 'Windows_NT'" DestinationFolder="$(TargetDir)" SourceFiles="$(MSBuildThisFileDirectory)src\admin\keys\Longbow.lic" SkipUnchangedFiles="true" />
<Message Text="Copy db file -> $(TargetDir)" Importance="high" />
<Copy Condition="'$(OS)' == 'UNIX'" DestinationFolder="$(TargetDir)" SourceFiles="$(MSBuildThisFileDirectory)src/admin/keys/Longbow.lic" SkipUnchangedFiles="true" />
</Target>
</Project> </Project>

View File

@ -611,6 +611,30 @@
"IsResource": NumberInt(0), "IsResource": NumberInt(0),
"Application": "BA" "Application": "BA"
}, },
{
"_id": ObjectId("1bd7b8445fa31256f77e4b9c"),
"ParentId": "5bd7b8445fa31256f77e4b9a",
"Name": "暂停",
"Order": NumberInt(20),
"Icon": "fa fa-fa",
"Url": "pause",
"Category": "0",
"Target": "_self",
"IsResource": NumberInt(2),
"Application": "BA"
},
{
"_id": ObjectId("1bd7b8445fa31256f77e4b9d"),
"ParentId": "5bd7b8445fa31256f77e4b9a",
"Name": "日志",
"Order": NumberInt(30),
"Icon": "fa fa-fa",
"Url": "info",
"Category": "0",
"Target": "_self",
"IsResource": NumberInt(2),
"Application": "BA"
},
{ {
"_id": ObjectId("5bd7b8445fa31256f77e4b9b"), "_id": ObjectId("5bd7b8445fa31256f77e4b9b"),
"ParentId": "0", "ParentId": "0",

View File

@ -110,6 +110,8 @@ INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category, IsResourc
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category, IsResource) VALUES (@@identity - 2, '删除', 30, 'fa fa-fa', 'del', '0', 2); INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category, IsResource) VALUES (@@identity - 2, '删除', 30, 'fa fa-fa', 'del', '0', 2);
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0');
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0');
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category, IsResource) VALUES (@@identity, '暂停', 10, 'fa fa-fa', 'pause', '0', 2);
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category, IsResource) VALUES (@@identity - 1, '日志', 20, 'fa fa-fa', 'info', '0', 2);
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0');
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0'); INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0');
INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (@@identity, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); INSERT INTO Navigations (ParentId, Name, `Order`, Icon, Url, Category) VALUES (@@identity, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0');

9
db/MySQL/install.ps1 Normal file
View File

@ -0,0 +1,9 @@
# init mysql database
$startPath = $args[0]
if ($startPath -eq $null) {
$startPath = "Z:\src\Longbow\BootstrapAdmin\db\SqlServer"
}
mysql -e "drop database if exists BootstrapAdmin; create database BootstrapAdmin;" -uroot
mysql -hlocalhost -uroot -DBootstrapAdmin < $startPath\install.sql
mysql -hlocalhost -uroot -DBootstrapAdmin < $startPath\initData.sql

View File

@ -111,6 +111,8 @@ INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (currval('navigations_id_seq') - 3, '删除', 30, 'fa fa-fa', 'del', '0', 2); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (currval('navigations_id_seq') - 3, '删除', 30, 'fa fa-fa', 'del', '0', 2);
INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0');
INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0');
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (currval('navigations_id_seq') - 1, '暂停', 10, 'fa fa-fa', 'pause', '0', 2);
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (currval('navigations_id_seq') - 2, '日志', 20, 'fa fa-fa', 'info', '0', 2);
INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0');
INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0');
INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (currval('navigations_id_seq') - 1, 0, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (currval('navigations_id_seq') - 1, 0, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0');

View File

@ -110,6 +110,8 @@ INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (last_insert_rowid() - 2, '删除', 30, 'fa fa-fa', 'del', '0', 2); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (last_insert_rowid() - 2, '删除', 30, 'fa fa-fa', 'del', '0', 2);
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0');
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0');
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (last_insert_rowid(), '暂停', 10, 'fa fa-fa', 'pause', '0', 2);
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (last_insert_rowid() - 1, '日志', 20, 'fa fa-fa', 'info', '0', 2);
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0');
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0'); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0');
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (last_insert_rowid(), '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (last_insert_rowid(), '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0');

View File

@ -113,6 +113,8 @@ INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (@@Identity - 2, N'删除', 30, 'fa fa-fa', 'del', '0', 2); INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (@@Identity - 2, N'删除', 30, 'fa fa-fa', 'del', '0', 2);
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'站内消息', 100, N'fa fa-envelope', N'~/Admin/Messages', N'0') INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'站内消息', 100, N'fa fa-envelope', N'~/Admin/Messages', N'0')
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'任务管理', 110, N'fa fa fa-tasks', N'~/Admin/Tasks', N'0') INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'任务管理', 110, N'fa fa fa-tasks', N'~/Admin/Tasks', N'0')
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (@@Identity, N'暂停', 10, 'fa fa-fa', 'pause', '0', 2);
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category], IsResource) VALUES (@@Identity - 1, N'日志', 20, 'fa fa-fa', 'info', '0', 2);
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'通知管理', 120, N'fa fa-bell', N'~/Admin/Notifications', N'0') INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'通知管理', 120, N'fa fa-bell', N'~/Admin/Notifications', N'0')
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'日志管理', 130, N'fa fa-gears', N'#', N'0') INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (0, N'日志管理', 130, N'fa fa-gears', N'#', N'0')
INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (@@Identity, N'操作日志', 10, N'fa fa-edit', N'~/Admin/Logs', N'0') INSERT INTO [Navigations] ([ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (@@Identity, N'操作日志', 10, N'fa fa-edit', N'~/Admin/Logs', N'0')

13
src/Directory.Build.props Normal file
View File

@ -0,0 +1,13 @@
<Project>
<Import Project="..\Directory.Build.props" />
<PropertyGroup>
<PackageProjectUrl>https://gitee.com/LongbowGroup/$(MsBuildProjectName)</PackageProjectUrl>
<RepositoryUrl>https://gitee.com/LongbowGroup/$(MsBuildProjectName).git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Nullable>enable</Nullable>
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
</PropertyGroup>
</Project>

View File

@ -1,14 +1,6 @@
<Router AppAssembly="@typeof(Program).Assembly"> <Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData"> <Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<NotAuthorized>
<NotAuthorizedHandler></NotAuthorizedHandler>
</NotAuthorized>
<Authorizing>
<h1>Authentication in progress</h1>
<p>Only visible while authentication is in progress.</p>
</Authorizing>
</AuthorizeRouteView>
</Found> </Found>
<NotFound> <NotFound>
<CascadingAuthenticationState> <CascadingAuthenticationState>

View File

@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Longbow.Logging" Version="3.0.2-beta1" /> <PackageReference Include="Longbow.Logging" Version="3.0.3-beta1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.0.0" />
<PackageReference Include="Sentry.AspNetCore" Version="2.0.0-beta6" /> <PackageReference Include="Sentry.AspNetCore" Version="2.0.0-beta6" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />

View File

@ -73,7 +73,7 @@ namespace Bootstrap.Admin.Controllers
/// <param name="appId"></param> /// <param name="appId"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
public ActionResult Login([FromQuery]string appId = null) public ActionResult Login([FromQuery]string? appId = null)
{ {
if (DictHelper.RetrieveSystemModel()) if (DictHelper.RetrieveSystemModel())
{ {
@ -111,11 +111,14 @@ namespace Bootstrap.Admin.Controllers
Description = "手机用户", Description = "手机用户",
App = provider.Options.App App = provider.Options.App
}; };
UserHelper.Save(user); if (UserHelper.Save(user) && !string.IsNullOrEmpty(user.Id))
{
// 根据配置文件设置默认角色 // 根据配置文件设置默认角色
var roles = RoleHelper.Retrieves().Where(r => provider.Options.Roles.Any(rl => rl.Equals(r.RoleName, StringComparison.OrdinalIgnoreCase))).Select(r => r.Id); var roles = RoleHelper.Retrieves().Where(r => provider.Options.Roles.Any(rl => rl.Equals(r.RoleName, StringComparison.OrdinalIgnoreCase))).Select(r => r.Id);
#pragma warning disable CS8620 // 由于引用类型的可为 null 性差异,实参不能用于形参。
RoleHelper.SaveByUserId(user.Id, roles); RoleHelper.SaveByUserId(user.Id, roles);
#pragma warning restore CS8620 // 由于引用类型的可为 null 性差异,实参不能用于形参。
}
} }
} }
return auth ? await SignInAsync(phone, true, MobileSchema) : RedirectLogin(); return auth ? await SignInAsync(phone, true, MobileSchema) : RedirectLogin();

View File

@ -8,7 +8,7 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 网站分析控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -16,11 +16,11 @@ namespace Bootstrap.Admin.Controllers.Api
public class AnalyseController : ControllerBase public class AnalyseController : ControllerBase
{ {
/// <summary> /// <summary>
/// /// 通过 logType 查询分析数据接口
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet()] [HttpGet()]
public ActionResult<AnalyseData> Get([FromQuery]string logType = "") public ActionResult<AnalyseData> Get([FromQuery]string logType)
{ {
var ret = new AnalyseData(); var ret = new AnalyseData();
if (logType.Equals("LoginUsers", StringComparison.OrdinalIgnoreCase)) if (logType.Equals("LoginUsers", StringComparison.OrdinalIgnoreCase))
@ -60,19 +60,19 @@ namespace Bootstrap.Admin.Controllers.Api
} }
/// <summary> /// <summary>
/// /// 分析数据实体类
/// </summary> /// </summary>
public class AnalyseData public class AnalyseData
{ {
/// <summary> /// <summary>
/// /// 获得/设置 折线数据集合
/// </summary> /// </summary>
public IEnumerable<string> Polylines { get; set; } public IEnumerable<string> Polylines { get; set; } = new string[0];
/// <summary> /// <summary>
/// /// 获得 数据集合
/// </summary> /// </summary>
public List<KeyValuePair<string, string>> Datas { get; set; } = new List<KeyValuePair<string, string>>(); public List<KeyValuePair<string, string>> Datas { get; } = new List<KeyValuePair<string, string>>();
} }
} }
} }

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 应用程序控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -14,7 +14,7 @@ namespace Bootstrap.Admin.Controllers.Api
public class AppsController : ControllerBase public class AppsController : ControllerBase
{ {
/// <summary> /// <summary>
/// /// 通过角色ID获取其授权的所有应用程序集合
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>

View File

@ -15,7 +15,7 @@ namespace Bootstrap.Admin.Controllers.Api
public class CategoryController : ControllerBase public class CategoryController : ControllerBase
{ {
/// <summary> /// <summary>
/// 获取字典表中所有Category数据 /// 获取字典表中所有 Category 数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
@ -26,7 +26,7 @@ namespace Bootstrap.Admin.Controllers.Api
} }
/// <summary> /// <summary>
/// /// 获取所有菜单数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
@ -36,7 +36,7 @@ namespace Bootstrap.Admin.Controllers.Api
} }
/// <summary> /// <summary>
/// /// 获取所有父级菜单数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
@ -44,5 +44,25 @@ namespace Bootstrap.Admin.Controllers.Api
{ {
return MenuHelper.RetrieveMenus(User.Identity.Name).Where(m => m.Menus.Count() > 0).OrderBy(m => m.Name).Select(m => m.Name); return MenuHelper.RetrieveMenus(User.Identity.Name).Where(m => m.Menus.Count() > 0).OrderBy(m => m.Name).Select(m => m.Name);
} }
/// <summary>
/// 通过指定菜单检查子菜单是否有子菜单
/// </summary>
/// <returns></returns>
[HttpGet("{id}")]
public bool ValidateMenuBySubMenu(string id)
{
return !MenuHelper.RetrieveAllMenus(User.Identity.Name).Where(m => m.ParentId == id).Any();
}
/// <summary>
/// 通过指定菜单检查父级菜单是否为菜单类型 资源与按钮返回 false
/// </summary>
/// <returns></returns>
[HttpGet("{id}")]
public bool ValidateParentMenuById(string id)
{
return MenuHelper.RetrieveAllMenus(User.Identity.Name).FirstOrDefault(m => m.Id == id)?.IsResource == 0;
}
} }
} }

View File

@ -10,7 +10,7 @@ using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 字典表维护控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -18,7 +18,7 @@ namespace Bootstrap.Admin.Controllers.Api
public class DictsController : ControllerBase public class DictsController : ControllerBase
{ {
/// <summary> /// <summary>
/// /// 获取所有字典表数据方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
@ -28,7 +28,7 @@ namespace Bootstrap.Admin.Controllers.Api
return value.RetrieveData(); return value.RetrieveData();
} }
/// <summary> /// <summary>
/// /// 保存字典方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
[HttpPost] [HttpPost]
@ -38,7 +38,7 @@ namespace Bootstrap.Admin.Controllers.Api
return DictHelper.Save(value); return DictHelper.Save(value);
} }
/// <summary> /// <summary>
/// /// 删除字典项方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
[HttpDelete] [HttpDelete]

View File

@ -57,12 +57,14 @@ namespace Bootstrap.Admin.Controllers.Api
var filePath = Path.Combine(AppContext.BaseDirectory, "Error"); var filePath = Path.Combine(AppContext.BaseDirectory, "Error");
var logName = $"{Path.Combine(filePath, exceptionFile.FileName)}.log"; var logName = $"{Path.Combine(filePath, exceptionFile.FileName)}.log";
if (!System.IO.File.Exists(logName)) return new JsonResult("无此日志文件"); if (!System.IO.File.Exists(logName)) return new JsonResult("无此日志文件");
StringBuilder sb = new StringBuilder(); var sb = new StringBuilder();
using (StreamReader reader = new StreamReader(logName)) using (var reader = new StreamReader(logName))
{ {
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
var line = reader.ReadLine().Replace("<", "&lt;").Replace(">", "&gt;"); var line = reader.ReadLine()?.Replace("<", "&lt;").Replace(">", "&gt;");
if (!string.IsNullOrEmpty(line))
{
if (line == "General Information ") sb.AppendFormat("<h4><b>{0}</b></h4>", line); if (line == "General Information ") sb.AppendFormat("<h4><b>{0}</b></h4>", line);
else if (line.StartsWith("TimeStamp:")) sb.AppendFormat("<div class='logTs'>{0}</div>", line); else if (line.StartsWith("TimeStamp:")) sb.AppendFormat("<div class='logTs'>{0}</div>", line);
else if (line.EndsWith("Exception Information")) sb.AppendFormat("<div class='logExcep'>{0}</div>", line); else if (line.EndsWith("Exception Information")) sb.AppendFormat("<div class='logExcep'>{0}</div>", line);
@ -73,18 +75,19 @@ namespace Bootstrap.Admin.Controllers.Api
else sb.AppendFormat("{0}<br>", line); else sb.AppendFormat("{0}<br>", line);
} }
} }
}
return new JsonResult(sb.ToString()); return new JsonResult(sb.ToString());
} }
/// <summary> /// <summary>
/// /// 查询服务器端日志文件参数类
/// </summary> /// </summary>
public class ExceptionFileQuery public class ExceptionFileQuery
{ {
/// <summary> /// <summary>
/// /// 获取/设置 文件名称
/// </summary> /// </summary>
public string FileName { get; set; } public string FileName { get; set; } = "";
} }
} }
} }

View File

@ -27,7 +27,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <param name="color"></param> /// <param name="color"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
public async Task<ActionResult> Issues([FromServices]GiteeHttpClient client, [FromQuery]string userName = "LongbowEnterprise", [FromQuery]string repoName = "BootstrapAdmin", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange") public async Task<ActionResult> Issues([FromServices]GiteeHttpClient client, [FromQuery]string? userName = "LongbowEnterprise", [FromQuery]string? repoName = "BootstrapAdmin", [FromQuery]string? label = "custom badge", [FromQuery]string? color = "orange")
{ {
var ret = await GetJsonAsync(() => client.HttpClient.GetStringAsync($"https://gitee.com/{userName}/{repoName}/issues"), content => var ret = await GetJsonAsync(() => client.HttpClient.GetStringAsync($"https://gitee.com/{userName}/{repoName}/issues"), content =>
{ {
@ -50,7 +50,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <param name="color"></param> /// <param name="color"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
public async Task<ActionResult> Pulls([FromServices]GiteeHttpClient client, [FromQuery]string userName = "LongbowEnterprise", [FromQuery]string repoName = "BootstrapAdmin", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange") public async Task<ActionResult> Pulls([FromServices]GiteeHttpClient client, [FromQuery]string? userName = "LongbowEnterprise", [FromQuery]string? repoName = "BootstrapAdmin", [FromQuery]string? label = "custom badge", [FromQuery]string? color = "orange")
{ {
var ret = await GetJsonAsync(() => client.HttpClient.GetStringAsync($"https://gitee.com/{userName}/{repoName}/pulls"), content => var ret = await GetJsonAsync(() => client.HttpClient.GetStringAsync($"https://gitee.com/{userName}/{repoName}/pulls"), content =>
{ {
@ -72,7 +72,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <param name="color"></param> /// <param name="color"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
public async Task<ActionResult> Releases([FromServices]GiteeHttpClient client, [FromQuery]string userName = "LongbowEnterprise", [FromQuery]string repoName = "BootstrapAdmin", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange") public async Task<ActionResult> Releases([FromServices]GiteeHttpClient client, [FromQuery]string? userName = "LongbowEnterprise", [FromQuery]string? repoName = "BootstrapAdmin", [FromQuery]string? label = "custom badge", [FromQuery]string? color = "orange")
{ {
var ret = await GetJsonAsync(() => client.HttpClient.GetStringAsync($"https://gitee.com/{userName}/{repoName}/releases"), content => var ret = await GetJsonAsync(() => client.HttpClient.GetStringAsync($"https://gitee.com/{userName}/{repoName}/releases"), content =>
{ {
@ -93,7 +93,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <param name="color"></param> /// <param name="color"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
public async Task<ActionResult> Builds([FromServices]GiteeHttpClient client, [FromQuery]string userName = "ArgoZhang", [FromQuery]string projName = "bootstrapadmin", [FromQuery]string branchName = "master", [FromQuery]string label = "custom badge", [FromQuery]string color = "orange") public async Task<ActionResult> Builds([FromServices]GiteeHttpClient client, [FromQuery]string? userName = "ArgoZhang", [FromQuery]string? projName = "bootstrapadmin", [FromQuery]string? branchName = "master", [FromQuery]string? label = "custom badge", [FromQuery]string? color = "orange")
{ {
var ret = await GetJsonAsync(() => client.HttpClient.GetAsJsonAsync<AppveyorBuildResult>($"https://ci.appveyor.com/api/projects/{userName}/{projName}/branch/{branchName}", null, new CancellationTokenSource(10000).Token), content => var ret = await GetJsonAsync(() => client.HttpClient.GetAsJsonAsync<AppveyorBuildResult>($"https://ci.appveyor.com/api/projects/{userName}/{projName}/branch/{branchName}", null, new CancellationTokenSource(10000).Token), content =>
{ {
@ -126,7 +126,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <summary> /// <summary>
/// Appveyor 编译版本实例 /// Appveyor 编译版本实例
/// </summary> /// </summary>
public Build Build { get; set; } public Build Build { get; set; } = new Build();
} }
private class Build private class Build
@ -134,7 +134,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <summary> /// <summary>
/// Build 版本信息 /// Build 版本信息
/// </summary> /// </summary>
public string Version { get; set; } public string Version { get; set; } = "";
} }
} }
} }

View File

@ -5,12 +5,11 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 部门维护控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -18,7 +17,7 @@ namespace Bootstrap.Admin.Controllers.Api
public class GroupsController : ControllerBase public class GroupsController : ControllerBase
{ {
/// <summary> /// <summary>
/// /// 部门数据查询方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
@ -29,18 +28,7 @@ namespace Bootstrap.Admin.Controllers.Api
} }
/// <summary> /// <summary>
/// /// 保存部门方法
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}")]
public Group Get(string id)
{
return GroupHelper.Retrieves().FirstOrDefault(t => t.Id == id);
}
/// <summary>
///
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
[HttpPost] [HttpPost]
@ -51,7 +39,7 @@ namespace Bootstrap.Admin.Controllers.Api
} }
/// <summary> /// <summary>
/// /// 删除部门方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
[HttpDelete] [HttpDelete]
@ -64,7 +52,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <summary> /// <summary>
/// 获取部门授权 /// 获取部门授权
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id">用户ID或者角色ID</param>
/// <param name="type"></param> /// <param name="type"></param>
/// <returns></returns> /// <returns></returns>
[HttpPost("{id}")] [HttpPost("{id}")]

View File

@ -3,7 +3,10 @@ using Bootstrap.Security;
using Bootstrap.Security.Mvc; using Bootstrap.Security.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers namespace Bootstrap.Admin.Controllers
{ {
@ -50,7 +53,7 @@ namespace Bootstrap.Admin.Controllers
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpPost] [HttpPost]
public BootstrapUser RetrieveUserByUserName([FromBody]string userName) public BootstrapUser? RetrieveUserByUserName([FromBody]string userName)
{ {
return UserHelper.RetrieveUserByUserName(userName); return UserHelper.RetrieveUserByUserName(userName);
} }
@ -63,7 +66,28 @@ namespace Bootstrap.Admin.Controllers
[HttpPost] [HttpPost]
public IEnumerable<BootstrapMenu> RetrieveAppMenus([FromBody]AppMenuOption args) public IEnumerable<BootstrapMenu> RetrieveAppMenus([FromBody]AppMenuOption args)
{ {
if (string.IsNullOrEmpty(args.AppId) || string.IsNullOrEmpty(args.UserName)) return new BootstrapMenu[0];
return MenuHelper.RetrieveAppMenus(args.AppId, args.UserName, args.Url); return MenuHelper.RetrieveAppMenus(args.AppId, args.UserName, args.Url);
} }
/// <summary>
/// 发送健康检查结果
/// </summary>
/// <param name="httpClient"></param>
/// <param name="config"></param>
/// <param name="message"></param>
/// <returns></returns>
[HttpPost]
public async Task<bool> Healths([FromServices]GiteeHttpClient httpClient, [FromServices]IConfiguration config, [FromBody]string message)
{
var ret = false;
var url = config.GetValue("HealthsCloudUrl", "");
if (!string.IsNullOrEmpty(url))
{
try { await httpClient.HttpClient.PostAsJsonAsync(url, message); ret = true; }
catch { }
}
return ret;
}
} }
} }

View File

@ -32,7 +32,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <param name="user"></param> /// <param name="user"></param>
/// <returns></returns> /// <returns></returns>
[HttpPost] [HttpPost]
public string Post([FromBody]User user) public string? Post([FromBody]User user)
{ {
var token = string.Empty; var token = string.Empty;
string userName = user.UserName; string userName = user.UserName;
@ -59,7 +59,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpOptions] [HttpOptions]
public string Options() public string? Options()
{ {
return null; return null;
} }

View File

@ -42,7 +42,7 @@ namespace Bootstrap.Admin.Controllers.Api
value.Browser = $"{agent.Browser?.Name} {agent.Browser?.Version}"; value.Browser = $"{agent.Browser?.Name} {agent.Browser?.Version}";
value.OS = $"{agent.OS?.Name} {agent.OS?.Version}"; value.OS = $"{agent.OS?.Name} {agent.OS?.Version}";
value.City = ipLocator.Locate(value.Ip); value.City = ipLocator.Locate(value.Ip);
value.UserName = User.Identity.Name; value.UserName = User.Identity.Name ?? string.Empty;
return LogHelper.Save(value); return LogHelper.Save(value);
} }
} }

View File

@ -7,7 +7,7 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers namespace Bootstrap.Admin.Controllers
{ {
/// <summary> /// <summary>
/// /// 新用户注册控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -38,13 +38,17 @@ namespace Bootstrap.Admin.Controllers
public bool Put([FromBody]User value) public bool Put([FromBody]User value)
{ {
var ret = false; var ret = false;
var userName = User.Identity.Name;
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(value.Id))
{
if (value.UserStatus == UserStates.ApproveUser) if (value.UserStatus == UserStates.ApproveUser)
{ {
ret = UserHelper.Approve(value.Id, User.Identity.Name); ret = UserHelper.Approve(value.Id, userName);
} }
else if (value.UserStatus == UserStates.RejectUser) else if (value.UserStatus == UserStates.RejectUser)
{ {
ret = UserHelper.Reject(value.Id, User.Identity.Name); ret = UserHelper.Reject(value.Id, userName);
}
} }
return ret; return ret;
} }

View File

@ -56,7 +56,7 @@ namespace Bootstrap.Admin.Controllers.Api
apps = apps.Take(6); apps = apps.Take(6);
apps.AsParallel().ForAll(n => apps.AsParallel().ForAll(n =>
{ {
n.ExceptionType = n.ExceptionType.Split('.').Last(); n.ExceptionType = n.ExceptionType?.Split('.').Last();
var ts = DateTime.Now - n.LogTime; var ts = DateTime.Now - n.LogTime;
if (ts.TotalMinutes < 5) n.Period = "刚刚"; if (ts.TotalMinutes < 5) n.Period = "刚刚";
else if (ts.Days > 0) n.Period = string.Format("{0}天", ts.Days); else if (ts.Days > 0) n.Period = string.Format("{0}天", ts.Days);

View File

@ -72,7 +72,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string Ip { get; set; } public string? Ip { get; set; }
/// <summary> /// <summary>
/// ///
@ -108,7 +108,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// </summary> /// </summary>
public void Reset() public void Reset()
{ {
if (dispatcher != null) dispatcher.Change(TimeSpan.FromSeconds(30), Timeout.InfiniteTimeSpan); dispatcher.Change(TimeSpan.FromSeconds(30), Timeout.InfiniteTimeSpan);
} }
#region Impletement IDispose #region Impletement IDispose
@ -123,7 +123,6 @@ namespace Bootstrap.Admin.Controllers.Api
if (dispatcher != null) if (dispatcher != null)
{ {
dispatcher.Dispose(); dispatcher.Dispose();
dispatcher = null;
} }
} }
} }

View File

@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 个人中心控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -30,9 +30,6 @@ namespace Bootstrap.Admin.Controllers.Api
public JsonResult Post(string id, [FromServices]IWebHostEnvironment env, [FromForm]DeleteFileCollection files) public JsonResult Post(string id, [FromServices]IWebHostEnvironment env, [FromForm]DeleteFileCollection files)
{ {
if (!id.Equals("Delete", StringComparison.OrdinalIgnoreCase)) return new JsonResult(new object()); if (!id.Equals("Delete", StringComparison.OrdinalIgnoreCase)) return new JsonResult(new object());
var previewUrl = string.Empty;
long fileSize = 0;
var userName = User.Identity.Name; var userName = User.Identity.Name;
var fileName = files.Key; var fileName = files.Key;
@ -41,10 +38,10 @@ namespace Bootstrap.Admin.Controllers.Api
fileName = "default.jpg"; fileName = "default.jpg";
var webSiteUrl = DictHelper.RetrieveIconFolderPath(); var webSiteUrl = DictHelper.RetrieveIconFolderPath();
var filePath = Path.Combine(env.WebRootPath, webSiteUrl.Replace("~", string.Empty).Replace('/', Path.DirectorySeparatorChar).TrimStart(Path.DirectorySeparatorChar) + fileName); var filePath = Path.Combine(env.WebRootPath, webSiteUrl.Replace("~", string.Empty).Replace('/', Path.DirectorySeparatorChar).TrimStart(Path.DirectorySeparatorChar) + fileName);
fileSize = new FileInfo(filePath).Length; var fileSize = new FileInfo(filePath).Length;
var iconName = $"{fileName}?v={DateTime.Now.Ticks}"; var iconName = $"{fileName}?v={DateTime.Now.Ticks}";
previewUrl = Url.Content($"{webSiteUrl}{iconName}"); var previewUrl = Url.Content($"{webSiteUrl}{iconName}");
UserHelper.SaveUserIconByName(userName, iconName); if (!string.IsNullOrEmpty(userName)) UserHelper.SaveUserIconByName(userName, iconName);
return new JsonResult(new return new JsonResult(new
{ {
@ -57,14 +54,14 @@ namespace Bootstrap.Admin.Controllers.Api
} }
/// <summary> /// <summary>
/// /// 待删除文件集合类
/// </summary> /// </summary>
public class DeleteFileCollection public class DeleteFileCollection
{ {
/// <summary> /// <summary>
/// /// 获得/设置 文件名称
/// </summary> /// </summary>
public string Key { get; set; } public string Key { get; set; } = "";
} }
/// <summary> /// <summary>
@ -96,7 +93,7 @@ namespace Bootstrap.Admin.Controllers.Api
} }
var iconName = $"{fileName}?v={DateTime.Now.Ticks}"; var iconName = $"{fileName}?v={DateTime.Now.Ticks}";
previewUrl = Url.Content($"{webSiteUrl}{iconName}"); previewUrl = Url.Content($"{webSiteUrl}{iconName}");
UserHelper.SaveUserIconByName(userName, iconName); if (!string.IsNullOrEmpty(userName)) UserHelper.SaveUserIconByName(userName, iconName);
} }
return new JsonResult(new return new JsonResult(new
{ {
@ -109,7 +106,7 @@ namespace Bootstrap.Admin.Controllers.Api
} }
/// <summary> /// <summary>
/// /// 个人中心操作方法 更改样式 更改显示名称 更改默认应用
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpPut] [HttpPut]

View File

@ -11,7 +11,7 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 角色维护控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -19,7 +19,7 @@ namespace Bootstrap.Admin.Controllers.Api
public class RolesController : ControllerBase public class RolesController : ControllerBase
{ {
/// <summary> /// <summary>
/// /// 获取所有角色数据
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
@ -37,7 +37,7 @@ namespace Bootstrap.Admin.Controllers.Api
[HttpPost("{id}")] [HttpPost("{id}")]
public IEnumerable<object> Post(string id, [FromQuery]string type) public IEnumerable<object> Post(string id, [FromQuery]string type)
{ {
IEnumerable<Role> ret = new List<Role>(); IEnumerable<Role> ret = new Role[0];
switch (type) switch (type)
{ {
case "user": case "user":
@ -53,7 +53,7 @@ namespace Bootstrap.Admin.Controllers.Api
return ret.Select(m => new { m.Id, m.Checked, m.RoleName, m.Description }); return ret.Select(m => new { m.Id, m.Checked, m.RoleName, m.Description });
} }
/// <summary> /// <summary>
/// 保存角色 /// 保存角色授权方法
/// </summary> /// </summary>
/// <param name="id">角色ID</param> /// <param name="id">角色ID</param>
/// <param name="values">选中的ID集合</param> /// <param name="values">选中的ID集合</param>
@ -82,7 +82,7 @@ namespace Bootstrap.Admin.Controllers.Api
return ret; return ret;
} }
/// <summary> /// <summary>
/// /// 保存角色方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
[HttpPost] [HttpPost]
@ -92,7 +92,7 @@ namespace Bootstrap.Admin.Controllers.Api
return RoleHelper.Save(value); return RoleHelper.Save(value);
} }
/// <summary> /// <summary>
/// /// 删除角色方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
[HttpDelete] [HttpDelete]

View File

@ -9,7 +9,7 @@ using System.Collections.Generic;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 网站设置控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -17,7 +17,7 @@ namespace Bootstrap.Admin.Controllers.Api
public class SettingsController : ControllerBase public class SettingsController : ControllerBase
{ {
/// <summary> /// <summary>
/// /// 保存网站设置方法
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
@ -26,7 +26,7 @@ namespace Bootstrap.Admin.Controllers.Api
public bool Post([FromBody]BootstrapDict value) => DictHelper.SaveSettings(value); public bool Post([FromBody]BootstrapDict value) => DictHelper.SaveSettings(value);
/// <summary> /// <summary>
/// /// 获取网站缓存站点集合
/// </summary> /// </summary>
[HttpGet] [HttpGet]
public IEnumerable<ICacheCorsItem> Get() => CacheManager.CorsSites; public IEnumerable<ICacheCorsItem> Get() => CacheManager.CorsSites;

View File

@ -1,4 +1,5 @@
using Longbow.Tasks; using Bootstrap.DataAccess;
using Longbow.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic; using System.Collections.Generic;
@ -7,7 +8,7 @@ using System.Linq;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 任务管理控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -15,7 +16,7 @@ namespace Bootstrap.Admin.Controllers.Api
public class TasksController : ControllerBase public class TasksController : ControllerBase
{ {
/// <summary> /// <summary>
/// /// 获取所有任务数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
@ -23,5 +24,25 @@ namespace Bootstrap.Admin.Controllers.Api
{ {
return TaskServicesManager.ToList().Select(s => new { s.Name, Status = s.Status.ToString(), s.LastRuntime, s.CreatedTime, s.NextRuntime, LastRunResult = s.Triggers.First().LastResult.ToString(), TriggerExpression = s.Triggers.FirstOrDefault().ToString() }).OrderBy(s => s.Name); return TaskServicesManager.ToList().Select(s => new { s.Name, Status = s.Status.ToString(), s.LastRuntime, s.CreatedTime, s.NextRuntime, LastRunResult = s.Triggers.First().LastResult.ToString(), TriggerExpression = s.Triggers.FirstOrDefault().ToString() }).OrderBy(s => s.Name);
} }
/// <summary>
/// 任务相关操作
/// </summary>
/// <param name="scheName">调度名称</param>
/// <param name="operType">操作方式 pause run</param>
[HttpPut("{scheName}")]
public bool Put(string scheName, [FromQuery]string operType)
{
var sche = TaskServicesManager.Get(scheName);
if (sche != null) sche.Status = operType == "pause" ? SchedulerStatus.Disabled : SchedulerStatus.Running;
// SQL 日志任务特殊处理
if (scheName == "SQL日志")
{
if (operType == "pause") LogHelper.Pause();
else LogHelper.Run();
}
return true;
}
} }
} }

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers.Api namespace Bootstrap.Admin.Controllers.Api
{ {
/// <summary> /// <summary>
/// /// 任务日志控制器
/// </summary> /// </summary>
[Route("api/[controller]")] [Route("api/[controller]")]
[Authorize] [Authorize]
@ -25,9 +25,14 @@ namespace Bootstrap.Admin.Controllers.Api
public ActionResult Get([FromQuery]string name, [FromServices]IHubContext<TaskLogHub> hub) public ActionResult Get([FromQuery]string name, [FromServices]IHubContext<TaskLogHub> hub)
{ {
var sche = TaskServicesManager.Get(name); var sche = TaskServicesManager.Get(name);
var ret = false;
if (sche != null)
{
sche.Triggers.First().PulseCallback = t => SendTaskLog(sche, name, hub); sche.Triggers.First().PulseCallback = t => SendTaskLog(sche, name, hub);
SendTaskLog(sche, name, hub); SendTaskLog(sche, name, hub);
return Ok(true); ret = true;
}
return Ok(ret);
} }
private Task SendTaskLog(IScheduler sche, string name, IHubContext<TaskLogHub> hub) private Task SendTaskLog(IScheduler sche, string name, IHubContext<TaskLogHub> hub)

View File

@ -38,7 +38,7 @@ namespace Bootstrap.Admin.Controllers.Api
[HttpPost("{id}")] [HttpPost("{id}")]
public IEnumerable<object> Post(string id, [FromQuery]string type) public IEnumerable<object> Post(string id, [FromQuery]string type)
{ {
IEnumerable<object> ret = null; IEnumerable<object> ret = new string[0];
switch (type) switch (type)
{ {
case "role": case "role":
@ -68,7 +68,7 @@ namespace Bootstrap.Admin.Controllers.Api
[ButtonAuthorize(Url = "~/Admin/Users", Auth = "add,edit")] [ButtonAuthorize(Url = "~/Admin/Users", Auth = "add,edit")]
public bool Post([FromBody]User value) public bool Post([FromBody]User value)
{ {
var ret = false; bool ret;
if (string.IsNullOrEmpty(value.Id)) if (string.IsNullOrEmpty(value.Id))
{ {
value.Description = string.Format("管理员{0}创建用户", User.Identity.Name); value.Description = string.Format("管理员{0}创建用户", User.Identity.Name);
@ -124,7 +124,7 @@ namespace Bootstrap.Admin.Controllers.Api
/// <returns></returns> /// <returns></returns>
[AllowAnonymous] [AllowAnonymous]
[HttpOptions] [HttpOptions]
public string Options() public string? Options()
{ {
return null; return null;
} }

View File

@ -22,7 +22,7 @@ namespace Bootstrap.Admin.Controllers
var model = new HeaderBarModel(User.Identity.Name); var model = new HeaderBarModel(User.Identity.Name);
var homeUrl = DictHelper.RetrieveHomeUrl(model.AppId); var homeUrl = DictHelper.RetrieveHomeUrl(model.AppId);
var useBlazor = configuration.GetValue("UseBlazor", false); var useBlazor = configuration.GetValue("UseBlazor", false);
return useBlazor ? Redirect("~/Admin/Home") : homeUrl.Equals("~/Home/Index", System.StringComparison.OrdinalIgnoreCase) ? (IActionResult)View(model) : Redirect(homeUrl); return useBlazor ? Redirect("~/Pages") : homeUrl.Equals("~/Home/Index", System.StringComparison.OrdinalIgnoreCase) ? (IActionResult)View(model) : Redirect(homeUrl);
} }
/// <summary> /// <summary>

View File

@ -2,9 +2,9 @@
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System;
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
{ {
@ -25,6 +25,12 @@ namespace Microsoft.AspNetCore.Builder
app.Use(async (context, next) => app.Use(async (context, next) =>
{ {
if (!_init) if (!_init)
{
// 优化性能
var config = context.RequestServices.GetRequiredService<IConfiguration>();
var autoGenerate = config.GetValue("AutoGenerateDatabase", false);
if (!autoGenerate) _init = true;
if (autoGenerate)
{ {
// 阻止所有线程继续往下运行,等待数据库检查 // 阻止所有线程继续往下运行,等待数据库检查
lock (_locker) lock (_locker)
@ -32,11 +38,10 @@ namespace Microsoft.AspNetCore.Builder
if (!_init) if (!_init)
{ {
// 数据检查 // 数据检查
var config = context.RequestServices.GetRequiredService<IConfiguration>();
var dbSection = config.GetSection("DB").GetChildren().FirstOrDefault(c => c.GetValue("Enabled", false)); var dbSection = config.GetSection("DB").GetChildren().FirstOrDefault(c => c.GetValue("Enabled", false));
if (dbSection != null) if (dbSection != null)
{ {
var folder = dbSection[SqlFolderKey]?.ReplaceOSPlatformPath(); var folder = dbSection.GetValue(SqlFolderKey, "").ReplaceOSPlatformPath();
if (!string.IsNullOrEmpty(folder)) if (!string.IsNullOrEmpty(folder))
{ {
// 判断文件夹是否存在 // 判断文件夹是否存在
@ -46,7 +51,7 @@ namespace Microsoft.AspNetCore.Builder
{ {
try try
{ {
AutoDbHelper.CheckDB(fullFolder); AutoDbHelper.EnsureCreated(fullFolder);
} }
catch { } catch { }
} }
@ -56,6 +61,7 @@ namespace Microsoft.AspNetCore.Builder
_init = true; _init = true;
} }
} }
}
await next(); await next();
}); });
return app; return app;

View File

@ -0,0 +1,88 @@
using Longbow.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// 邮件日志扩展方法
/// </summary>
public static class CloudLoggerExtensions
{
/// <summary>
/// 注册邮件日志方法
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static ILoggingBuilder AddCloudLogger(this ILoggingBuilder builder)
{
builder.Services.AddSingleton<IConfigureOptions<CloudLoggerOption>, LoggerProviderConfigureOptions<CloudLoggerOption, CloudLoggerProvider>>();
builder.Services.AddSingleton<IOptionsChangeTokenSource<CloudLoggerOption>, LoggerProviderOptionsChangeTokenSource<CloudLoggerOption, CloudLoggerProvider>>();
builder.Services.AddSingleton<ILoggerProvider, CloudLoggerProvider>();
return builder;
}
}
/// <summary>
/// 云日志提供类
/// </summary>
[ProviderAlias("Cloud")]
public class CloudLoggerProvider : LoggerProvider
{
private readonly HttpClient httpClient;
private readonly IDisposable optionsReloadToken;
private CloudLoggerOption option;
/// <summary>
/// 构造函数
/// </summary>
public CloudLoggerProvider(IOptionsMonitor<CloudLoggerOption> options) : base(null, new Func<string, LogLevel, bool>((name, logLevel) => logLevel >= LogLevel.Error))
{
optionsReloadToken = options.OnChange(op => option = op);
option = options.CurrentValue;
httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
httpClient.DefaultRequestHeaders.Connection.Add("keep-alive");
LogCallback = new Action<string>(async message =>
{
if (!string.IsNullOrEmpty(option.Url))
{
try { await httpClient.PostAsJsonAsync(option.Url, message).ConfigureAwait(false); }
catch { }
}
});
}
/// <summary>
///
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
httpClient.Dispose();
optionsReloadToken.Dispose();
}
}
}
/// <summary>
/// 云日志配置类
/// </summary>
public class CloudLoggerOption
{
/// <summary>
/// 获得/设置 云日志地址
/// </summary>
public string Url { get; set; } = "";
}
}

View File

@ -18,7 +18,7 @@ namespace Bootstrap.Admin.HealthChecks
{ {
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
private static readonly Func<IConfiguration, string, string> ConnectionStringResolve = (c, name) => string.IsNullOrEmpty(name) private static readonly Func<IConfiguration, string, string?> ConnectionStringResolve = (c, name) => string.IsNullOrEmpty(name)
? c.GetSection("ConnectionStrings").GetChildren().FirstOrDefault()?.Value ? c.GetSection("ConnectionStrings").GetChildren().FirstOrDefault()?.Value
: c.GetConnectionString(name); : c.GetConnectionString(name);
@ -42,13 +42,13 @@ namespace Bootstrap.Admin.HealthChecks
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{ {
var db = _configuration.GetSection("DB").GetChildren() var db = _configuration.GetSection("DB").GetChildren()
.Select(config => new .Select(config => new DbOption()
{ {
Enabled = bool.TryParse(config["Enabled"], out var en) ? en : false, Enabled = bool.TryParse(config["Enabled"], out var en) ? en : false,
ProviderName = config["ProviderName"], ProviderName = config["ProviderName"],
Widget = config["Widget"], Widget = config["Widget"],
ConnectionString = ConnectionStringResolve(config.GetSection("ConnectionStrings").Exists() ? config : _configuration, string.Empty) ConnectionString = ConnectionStringResolve(config.GetSection("ConnectionStrings").Exists() ? config : _configuration, string.Empty)
}).FirstOrDefault(i => i.Enabled) ?? new }).FirstOrDefault(i => i.Enabled) ?? new DbOption()
{ {
Enabled = true, Enabled = true,
ProviderName = Longbow.Data.DatabaseProviderType.SqlServer.ToString(), ProviderName = Longbow.Data.DatabaseProviderType.SqlServer.ToString(),
@ -64,12 +64,11 @@ namespace Bootstrap.Admin.HealthChecks
var roles = string.Empty; var roles = string.Empty;
var displayName = string.Empty; var displayName = string.Empty;
var healths = false; var healths = false;
Exception error = null; Exception? error = null;
try try
{ {
DbContextManager.Exception = null;
var user = UserHelper.RetrieveUserByUserName(userName); var user = UserHelper.RetrieveUserByUserName(userName);
displayName = user?.DisplayName; displayName = user?.DisplayName ?? string.Empty;
roles = string.Join(",", RoleHelper.RetrievesByUserName(userName) ?? new string[0]); roles = string.Join(",", RoleHelper.RetrievesByUserName(userName) ?? new string[0]);
menusCount = MenuHelper.RetrieveMenusByUserName(userName)?.Count() ?? 0; menusCount = MenuHelper.RetrieveMenusByUserName(userName)?.Count() ?? 0;
dictsCount = DictHelper.RetrieveDicts()?.Count() ?? 0; dictsCount = DictHelper.RetrieveDicts()?.Count() ?? 0;
@ -79,32 +78,41 @@ namespace Bootstrap.Admin.HealthChecks
{ {
error = ex; error = ex;
} }
var data = new Dictionary<string, object>() var data = new Dictionary<string, object?>()
{ {
{ "ConnectionString", db.ConnectionString }, { "ConnectionString", db.ConnectionString },
{ "Reference", DbContextManager.Create<Dict>()?.GetType().Assembly.FullName ?? db.Widget }, { "Reference", DbContextManager.Create<Dict>()?.GetType().Assembly.FullName ?? db.Widget },
{ "DbType", db?.ProviderName }, { "DbType", db?.ProviderName },
{ "Dicts", dictsCount }, { "Dicts", dictsCount },
{ "LoginName", loginUser }, { "LoginName", userName },
{ "DisplayName", displayName }, { "DisplayName", displayName },
{ "Roles", roles }, { "Roles", roles },
{ "Navigations", menusCount } { "Navigations", menusCount }
}; };
if (string.IsNullOrEmpty(db.ConnectionString)) if (string.IsNullOrEmpty(db?.ConnectionString))
{ {
// 未启用连接字符串 // 未启用连接字符串
data["ConnectionString"] = "未配置数据库连接字符串"; data["ConnectionString"] = "未配置数据库连接字符串";
return Task.FromResult(HealthCheckResult.Unhealthy("Error", null, data)); return Task.FromResult(HealthCheckResult.Unhealthy("Error", null, data));
} }
if (error != null || DbContextManager.Exception != null) if (DbContextManager.Exception != null) error = DbContextManager.Exception;
if (error != null)
{ {
data.Add("Exception", (DbContextManager.Exception ?? error).Message); data.Add("Exception", error.Message);
return Task.FromResult(HealthCheckResult.Unhealthy("Error", error ?? DbContextManager.Exception, data)); return Task.FromResult(HealthCheckResult.Unhealthy("Error", error, data));
} }
return healths ? Task.FromResult(HealthCheckResult.Healthy("Ok", data)) : Task.FromResult(HealthCheckResult.Degraded("Failed", null, data)); return healths ? Task.FromResult(HealthCheckResult.Healthy("Ok", data)) : Task.FromResult(HealthCheckResult.Degraded("Failed", null, data));
} }
private class DbOption
{
public bool Enabled { get; set; }
public string? ProviderName { get; set; }
public string? Widget { get; set; }
public string? ConnectionString { get; set; }
}
} }
} }

View File

@ -41,7 +41,7 @@ namespace Bootstrap.Admin.HealthChecks
Task.WaitAll(urls.Select(url => Task.Run(async () => Task.WaitAll(urls.Select(url => Task.Run(async () =>
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
Exception error = null; Exception? error = null;
var result = await _client.HttpClient.GetAsJsonAsync<object>($"/api/Gitee/{url}", ex => error = ex, cancellationToken); var result = await _client.HttpClient.GetAsJsonAsync<object>($"/api/Gitee/{url}", ex => error = ex, cancellationToken);
sw.Stop(); sw.Stop();
data.Add(url, error == null ? $"{result} Elapsed: {sw.Elapsed}" : $"{result} Elapsed: {sw.Elapsed} Exception: {error}"); data.Add(url, error == null ? $"{result} Elapsed: {sw.Elapsed}" : $"{result} Elapsed: {sw.Elapsed} Exception: {error}");

View File

@ -12,7 +12,7 @@ namespace Bootstrap.Admin.Models
/// 默认构造函数 /// 默认构造函数
/// </summary> /// </summary>
/// <param name="appId"></param> /// <param name="appId"></param>
public AdminModel(string appId = null) public AdminModel(string? appId = null)
{ {
if (string.IsNullOrEmpty(appId)) appId = BootstrapAppContext.AppId; if (string.IsNullOrEmpty(appId)) appId = BootstrapAppContext.AppId;

View File

@ -13,28 +13,28 @@
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; } = "";
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string Content { get; set; } public string Content { get; set; } = "";
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string Image { get; set; } public string Image { get; set; } = "";
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string Detail { get; set; } public string Detail { get; set; } = "";
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string ReturnUrl { get; set; } public string ReturnUrl { get; set; } = "";
/// <summary> /// <summary>
/// ///

View File

@ -13,7 +13,7 @@ namespace Bootstrap.Admin.Models
/// 默认构造函数 /// 默认构造函数
/// </summary> /// </summary>
/// <param name="userName"></param> /// <param name="userName"></param>
public HeaderBarModel(string userName) public HeaderBarModel(string? userName)
{ {
var user = UserHelper.RetrieveUserByUserName(userName); var user = UserHelper.RetrieveUserByUserName(userName);
if (user != null) if (user != null)
@ -42,31 +42,31 @@ namespace Bootstrap.Admin.Models
/// <summary> /// <summary>
/// 获得 当前用户登录名 /// 获得 当前用户登录名
/// </summary> /// </summary>
public string UserName { get; } public string UserName { get; } = "";
/// <summary> /// <summary>
/// 获得 当前用户显示名称 /// 获得 当前用户显示名称
/// </summary> /// </summary>
public string DisplayName { get; } public string DisplayName { get; } = "";
/// <summary> /// <summary>
/// 获得 用户头像地址 /// 获得 用户头像地址
/// </summary> /// </summary>
public string Icon { get; } public string Icon { get; } = "";
/// <summary> /// <summary>
/// 获取 个人网站样式 /// 获取 个人网站样式
/// </summary> /// </summary>
public string Css { get; } public string Css { get; } = "";
/// <summary> /// <summary>
/// 获得 当前设置的默认应用 /// 获得 当前设置的默认应用
/// </summary> /// </summary>
public string AppId { get; } public string AppId { get; } = "";
/// <summary> /// <summary>
/// 获得 当前样式 /// 获得 当前样式
/// </summary> /// </summary>
public string ActiveCss { get; } public string ActiveCss { get; } = "";
} }
} }

View File

@ -9,7 +9,7 @@
/// 构造函数 /// 构造函数
/// </summary> /// </summary>
/// <param name="userName"></param> /// <param name="userName"></param>
public LockModel(string userName) : base(userName) public LockModel(string? userName) : base(userName)
{ {
} }
@ -17,11 +17,11 @@
/// <summary> /// <summary>
/// 获得/设置 返回路径 /// 获得/设置 返回路径
/// </summary> /// </summary>
public string ReturnUrl { get; set; } public string? ReturnUrl { get; set; }
/// <summary> /// <summary>
/// 获得/设置 认证方式 Cookie Mobile Gitee GitHub /// 获得/设置 认证方式 Cookie Mobile Gitee GitHub
/// </summary> /// </summary>
public string AuthenticationType { get; set; } public string? AuthenticationType { get; set; }
} }
} }

View File

@ -11,7 +11,7 @@ namespace Bootstrap.Admin.Models
/// 默认构造函数 /// 默认构造函数
/// </summary> /// </summary>
/// <param name="appId"></param> /// <param name="appId"></param>
public LoginModel(string appId = null) : base(appId) public LoginModel(string? appId = null) : base(appId)
{ {
ImageLibUrl = DictHelper.RetrieveImagesLibUrl(); ImageLibUrl = DictHelper.RetrieveImagesLibUrl();
} }

View File

@ -26,7 +26,7 @@ namespace Bootstrap.Admin.Models
/// </summary> /// </summary>
/// <param name="userName"></param> /// <param name="userName"></param>
/// <param name="activeUrl"></param> /// <param name="activeUrl"></param>
public NavigatorBarModel(string userName, string activeUrl = "~/Admin/Index") : base(userName) public NavigatorBarModel(string? userName, string activeUrl = "~/Admin/Index") : base(userName)
{ {
Navigations = MenuHelper.RetrieveSystemMenus(UserName, activeUrl); Navigations = MenuHelper.RetrieveSystemMenus(UserName, activeUrl);
var authApps = AppHelper.RetrievesByUserName(userName); var authApps = AppHelper.RetrievesByUserName(userName);

View File

@ -18,7 +18,7 @@ namespace Bootstrap.Admin.Models
/// <summary> /// <summary>
/// 获得 头像文件名称 /// 获得 头像文件名称
/// </summary> /// </summary>
public string FileName { get; } public string FileName { get; } = "";
/// <summary> /// <summary>
/// 获得 是否为第三方用户 /// 获得 是否为第三方用户

View File

@ -1,4 +1,5 @@
@page "/Admin/Home" @page "/Pages/Admin/Index"
@page "/Pages"
<h3>Home</h3> <h3>Home</h3>

View File

@ -0,0 +1,6 @@
@page "/Pages/Admin/Roles"
<h3>Roles</h3>
@code {
}

View File

@ -0,0 +1,6 @@
@page "/Pages/Admin/Users"
<h3>Users</h3>
@code {
}

View File

@ -0,0 +1,22 @@
namespace Bootstrap.Admin.Pages
{
/// <summary>
/// Url 地址辅助操作类
/// </summary>
public static class UrlHelper
{
/// <summary>
/// 转换为 Blazor 地址
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string ToBlazorLink(this string url) => url.TrimStart('~');
/// <summary>
/// 转化为 Blazor 菜单地址
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string ToBlazorMenuUrl(this string url) => url.Replace("~", "/Pages");
}
}

View File

@ -1,4 +1,4 @@
@page "/" @page "/Pages"
@namespace Bootstrap.Admin.Pages @namespace Bootstrap.Admin.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -13,15 +13,18 @@ namespace Bootstrap.Admin.Query
/// <summary> /// <summary>
/// 字典分项 /// 字典分项
/// </summary> /// </summary>
public string Category { get; set; } public string? Category { get; set; }
/// <summary> /// <summary>
/// 字典名称 /// 字典名称
/// </summary> /// </summary>
public string Name { get; set; } public string? Name { get; set; }
/// <summary> /// <summary>
/// 字典种类 /// 字典种类
/// </summary> /// </summary>
public string Define { get; set; } public string? Define { get; set; }
/// <summary> /// <summary>
/// 字典表查询 /// 字典表查询
/// </summary> /// </summary>
@ -34,15 +37,15 @@ namespace Bootstrap.Admin.Query
var data = DictHelper.RetrieveDicts(); var data = DictHelper.RetrieveDicts();
if (!string.IsNullOrEmpty(Category)) if (!string.IsNullOrEmpty(Category))
{ {
data = data.Where(t => t.Category.Contains(Category)); data = data.Where(t => t.Category?.Contains(Category) ?? false);
} }
if (!string.IsNullOrEmpty(Name)) if (!string.IsNullOrEmpty(Name))
{ {
data = data.Where(t => t.Name.Contains(Name)); data = data.Where(t => t.Name?.Contains(Name) ?? false);
} }
if (!string.IsNullOrEmpty(Define)) if (!string.IsNullOrEmpty(Define))
{ {
data = data.Where(t => t.Define.ToString() == Define); data = data.Where(t => t.Define.ToString() == (Define ?? ""));
} }
var ret = new QueryData<BootstrapDict>(); var ret = new QueryData<BootstrapDict>();
ret.total = data.Count(); ret.total = data.Count();

View File

@ -6,20 +6,22 @@ using System.Linq;
namespace Bootstrap.Admin.Query namespace Bootstrap.Admin.Query
{ {
/// <summary> /// <summary>
/// /// 程序异常查询条件类
/// </summary> /// </summary>
public class QueryExceptionOption : PaginationOption public class QueryExceptionOption : PaginationOption
{ {
/// <summary> /// <summary>
/// /// 获得/设置 开始时间
/// </summary> /// </summary>
public DateTime? StartTime { get; set; } public DateTime? StartTime { get; set; }
/// <summary> /// <summary>
/// /// 获得/设置 结束时间
/// </summary> /// </summary>
public DateTime? EndTime { get; set; } public DateTime? EndTime { get; set; }
/// <summary> /// <summary>
/// /// 查询方法
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public QueryData<object> Retrieves() public QueryData<object> Retrieves()

View File

@ -5,20 +5,22 @@ using System.Linq;
namespace Bootstrap.Admin.Query namespace Bootstrap.Admin.Query
{ {
/// <summary> /// <summary>
/// /// 部门查询条件类
/// </summary> /// </summary>
public class QueryGroupOption : PaginationOption public class QueryGroupOption : PaginationOption
{ {
/// <summary> /// <summary>
/// /// 获得/设置 部门名称
/// </summary> /// </summary>
public string GroupName { get; set; } public string? GroupName { get; set; }
/// <summary> /// <summary>
/// /// 获得/设置 部门描述
/// </summary> /// </summary>
public string Description { get; set; } public string? Description { get; set; }
/// <summary> /// <summary>
/// /// 部门查询数据方法
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public QueryData<object> RetrieveData() public QueryData<object> RetrieveData()
@ -27,11 +29,11 @@ namespace Bootstrap.Admin.Query
var data = GroupHelper.Retrieves(); var data = GroupHelper.Retrieves();
if (!string.IsNullOrEmpty(GroupName)) if (!string.IsNullOrEmpty(GroupName))
{ {
data = data.Where(t => t.GroupName.Contains(GroupName)); data = data.Where(t => t.GroupName?.Contains(GroupName) ?? false);
} }
if (!string.IsNullOrEmpty(Description)) if (!string.IsNullOrEmpty(Description))
{ {
data = data.Where(t => t.Description.Contains(Description)); data = data.Where(t => t.Description?.Contains(Description) ?? false);
} }
var ret = new QueryData<object>(); var ret = new QueryData<object>();
ret.total = data.Count(); ret.total = data.Count();

View File

@ -5,27 +5,27 @@ using System;
namespace Bootstrap.Admin.Query namespace Bootstrap.Admin.Query
{ {
/// <summary> /// <summary>
/// /// 操作日志查询条件类
/// </summary> /// </summary>
public class QueryLogOption : PaginationOption public class QueryLogOption : PaginationOption
{ {
/// <summary> /// <summary>
/// /// 获得/设置 操作类型
/// </summary> /// </summary>
public string OperateType { get; set; } public string? OperateType { get; set; }
/// <summary> /// <summary>
/// /// 获得/设置 开始时间
/// </summary> /// </summary>
public DateTime? OperateTimeStart { get; set; } public DateTime? OperateTimeStart { get; set; }
/// <summary> /// <summary>
/// /// 获得/设置 结束时间
/// </summary> /// </summary>
public DateTime? OperateTimeEnd { get; set; } public DateTime? OperateTimeEnd { get; set; }
/// <summary> /// <summary>
/// /// 获得/设置 获取查询分页数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public QueryData<Log> RetrieveData() public QueryData<Log> RetrieveData()

View File

@ -22,7 +22,7 @@ namespace Bootstrap.Admin.Query
/// <summary> /// <summary>
/// 登录IP地址 /// 登录IP地址
/// </summary> /// </summary>
public string LoginIP { get; set; } public string? LoginIP { get; set; }
/// <summary> /// <summary>
/// ///
@ -30,8 +30,6 @@ namespace Bootstrap.Admin.Query
/// <returns></returns> /// <returns></returns>
public QueryData<LoginUser> RetrieveData() public QueryData<LoginUser> RetrieveData()
{ {
if (string.IsNullOrEmpty(Order)) Order = "desc";
if (string.IsNullOrEmpty(Sort)) Sort = "LoginTime";
var data = LoginHelper.RetrievePages(this, StartTime, EndTime, LoginIP); var data = LoginHelper.RetrievePages(this, StartTime, EndTime, LoginIP);
return new QueryData<LoginUser> return new QueryData<LoginUser>
{ {

View File

@ -13,34 +13,34 @@ namespace Bootstrap.Admin.Query
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string Name { get; set; } public string? Name { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string ParentName { get; set; } public string? ParentName { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string Category { get; set; } public string? Category { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string IsResource { get; set; } public string? IsResource { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string AppId { get; set; } public string? AppId { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="userName"></param> /// <param name="userName"></param>
/// <returns></returns> /// <returns></returns>
public QueryData<object> RetrieveData(string userName) public QueryData<object> RetrieveData(string? userName)
{ {
var data = MenuHelper.RetrieveMenusByUserName(userName); var data = MenuHelper.RetrieveMenusByUserName(userName);
if (!string.IsNullOrEmpty(ParentName)) if (!string.IsNullOrEmpty(ParentName))

View File

@ -5,20 +5,22 @@ using System.Linq;
namespace Bootstrap.Admin.Query namespace Bootstrap.Admin.Query
{ {
/// <summary> /// <summary>
/// /// 角色查询条件类
/// </summary> /// </summary>
public class QueryRoleOption : PaginationOption public class QueryRoleOption : PaginationOption
{ {
/// <summary> /// <summary>
/// /// 角色名称
/// </summary> /// </summary>
public string RoleName { get; set; } public string? RoleName { get; set; }
/// <summary> /// <summary>
/// /// 角色描述
/// </summary> /// </summary>
public string Description { get; set; } public string? Description { get; set; }
/// <summary> /// <summary>
/// /// 角色数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public QueryData<object> RetrieveData() public QueryData<object> RetrieveData()

View File

@ -12,7 +12,7 @@ namespace Bootstrap.Admin.Query
/// <summary> /// <summary>
/// 获得/设置 用户登录名 /// 获得/设置 用户登录名
/// </summary> /// </summary>
public string UserName { get; set; } public string? UserName { get; set; }
/// <summary> /// <summary>
/// 获得/设置 开始时间 /// 获得/设置 开始时间
@ -30,7 +30,6 @@ namespace Bootstrap.Admin.Query
/// <returns></returns> /// <returns></returns>
public QueryData<DBLog> RetrieveData() public QueryData<DBLog> RetrieveData()
{ {
if (string.IsNullOrEmpty(Order)) Order = "LogTime";
var data = LogHelper.RetrieveDBLogs(this, OperateTimeStart, OperateTimeEnd, UserName); var data = LogHelper.RetrieveDBLogs(this, OperateTimeStart, OperateTimeEnd, UserName);
var ret = new QueryData<DBLog>(); var ret = new QueryData<DBLog>();
ret.total = data.TotalItems; ret.total = data.TotalItems;

View File

@ -22,7 +22,7 @@ namespace Bootstrap.Admin.Query
/// <summary> /// <summary>
/// 请求IP地址 /// 请求IP地址
/// </summary> /// </summary>
public string AccessIP { get; set; } public string? AccessIP { get; set; }
/// <summary> /// <summary>
/// ///

View File

@ -5,20 +5,22 @@ using System.Linq;
namespace Bootstrap.Admin.Query namespace Bootstrap.Admin.Query
{ {
/// <summary> /// <summary>
/// /// 用户维护查询条件类
/// </summary> /// </summary>
public class QueryUserOption : PaginationOption public class QueryUserOption : PaginationOption
{ {
/// <summary> /// <summary>
/// /// 获得/设置 用户登录名称
/// </summary> /// </summary>
public string Name { get; set; } public string? Name { get; set; }
/// <summary> /// <summary>
/// /// 获得/设置 用户显示名称
/// </summary> /// </summary>
public string DisplayName { get; set; } public string? DisplayName { get; set; }
/// <summary> /// <summary>
/// /// 获取用户分页数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public QueryData<object> RetrieveData() public QueryData<object> RetrieveData()

View File

@ -1,16 +1,18 @@
<footer class="position-fixed"> @inject IConfiguration configuration
<div><span id="websiteFooter">@Model.Footer</span><span asp-condition="@Model.IsDemo">(演示系统)</span></div>
<footer class="position-fixed">
<div><span id="websiteFooter">@Model?.Footer</span><span asp-condition="@Model?.IsDemo">(演示系统)</span></div>
<a id="gotoTop" href="#" class="go-top" title="返回顶部" data-toggle="tooltip" data-placement="left"> <a id="gotoTop" href="#" class="go-top" title="返回顶部" data-toggle="tooltip" data-placement="left">
<i class="fa fa-angle-up"></i> <i class="fa fa-angle-up"></i>
</a> </a>
</footer> </footer>
@code { @code {
private AdminModel Model; private AdminModel? Model;
protected override Task OnInitializedAsync() protected override Task OnInitializedAsync()
{ {
Model = new AdminModel(); Model = new AdminModel(configuration.GetValue("AppId", "BA"));
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

View File

@ -5,10 +5,10 @@
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<a id="navbar" href="#" class="sidebar-toggle-box"> <a id="navbar" href="#" class="sidebar-toggle-box">
<i class="fa fa-bars"></i> <i class="fa fa-bars"></i>
<span id="websiteTitle">@Model.Title</span> <span id="websiteTitle">@Model?.Title</span>
</a> </a>
<div class="nav"> <div class="nav">
@if (User.IsInRole("Administrators")) @if (User?.IsInRole("Administrators") ?? false)
{ {
<!-- tasks start --> <!-- tasks start -->
<div class="dropdown"> <div class="dropdown">
@ -20,7 +20,7 @@
<div class="dropdown-arrow arrow-primary"></div> <div class="dropdown-arrow arrow-primary"></div>
<div id="msgHeaderTaskContent" class="dropdown-header bg-primary">您有 <span id="msgHeaderTask">0</span> 个未完成的任务</div> <div id="msgHeaderTaskContent" class="dropdown-header bg-primary">您有 <span id="msgHeaderTask">0</span> 个未完成的任务</div>
<div class="dropdown-footer"> <div class="dropdown-footer">
<a href="~/Admin/Tasks">查看所有任务</a> <a href="/Pages/Admin/Tasks">查看所有任务</a>
</div> </div>
</div> </div>
</div> </div>
@ -35,7 +35,7 @@
<div class="dropdown-arrow arrow-info"></div> <div class="dropdown-arrow arrow-info"></div>
<div id="msgHeaderMsgContent" class="dropdown-header bg-info">您有 <span id="msgHeaderMsg">0</span> 个未读的消息</div> <div id="msgHeaderMsgContent" class="dropdown-header bg-info">您有 <span id="msgHeaderMsg">0</span> 个未读的消息</div>
<div class="dropdown-footer"> <div class="dropdown-footer">
<a href="~/Admin/Messages">查看所有消息</a> <a href="/Pages/Admin/Messages">查看所有消息</a>
</div> </div>
</div> </div>
</div> </div>
@ -65,7 +65,7 @@
<div class="dropdown-arrow arrow-warning"></div> <div class="dropdown-arrow arrow-warning"></div>
<div id="msgHeaderAppContent" class="dropdown-header bg-warning">您有 <span id="msgHeaderApp">0</span> 条程序异常通知</div> <div id="msgHeaderAppContent" class="dropdown-header bg-warning">您有 <span id="msgHeaderApp">0</span> 条程序异常通知</div>
<div class="dropdown-footer"> <div class="dropdown-footer">
<a href="~/Admin/Exceptions">查看所有异常</a> <a href="/Pages/Admin/Exceptions">查看所有异常</a>
</div> </div>
</div> </div>
</div> </div>
@ -80,13 +80,13 @@
<div class="dropdown-arrow arrow-danger"></div> <div class="dropdown-arrow arrow-danger"></div>
<div id="msgHeaderDbContent" class="dropdown-header bg-danger">您有 <span id="msgHeaderDb">0</span> 条数据库异常通知</div> <div id="msgHeaderDbContent" class="dropdown-header bg-danger">您有 <span id="msgHeaderDb">0</span> 条数据库异常通知</div>
<div class="dropdown-footer"> <div class="dropdown-footer">
<a href="~/Admin/Exceptions">查看所有异常</a> <a href="/Pages/Admin/Exceptions">查看所有异常</a>
</div> </div>
</div> </div>
</div> </div>
<!-- db dropdown end --> <!-- db dropdown end -->
<div class="dropdown"> <div class="dropdown">
<a class="shadow-success" href="~/Account/Lock" data-toggle="tooltip" title="系统锁屏"> <a class="shadow-success" href="/Account/Lock" data-toggle="tooltip" title="系统锁屏">
<i class="fa fa-tv"></i> <i class="fa fa-tv"></i>
</a> </a>
</div> </div>
@ -94,41 +94,41 @@
</div> </div>
<div class="dropdown userinfo"> <div class="dropdown userinfo">
<a data-toggle="dropdown" class="dropdown-toggle shadow-default" href="#"> <a data-toggle="dropdown" class="dropdown-toggle shadow-default" href="#">
<img id="headerIcon" alt="" src="@Model.Icon" /> <img id="headerIcon" alt="" src="@Model?.Icon.ToBlazorLink()" />
<span id="userDisplayName" data-userName="@Model.UserName" class="username text-truncate d-inline-block">@Model.DisplayName</span> <span id="userDisplayName" data-userName="@Model?.UserName" class="username text-truncate d-inline-block">@Model?.DisplayName</span>
</a> </a>
<div class="dropdown-menu dropdown-menu-right"> <div class="dropdown-menu dropdown-menu-right">
<div class="dropdown-item"> <div class="dropdown-item">
<div class="d-flex flex-fill align-items-center"> <div class="d-flex flex-fill align-items-center">
<img src="@Model.Icon"> <img src="@Model?.Icon.ToBlazorLink()">
<div class="flex-fill"> <div class="flex-fill">
<div class="username text-truncate">@Model.DisplayName</div> <div class="username text-truncate">@Model?.DisplayName</div>
<div>登录名:@Model.UserName</div> <div>登录名:@Model?.UserName</div>
</div> </div>
</div> </div>
</div> </div>
<div class="dropdown-item"> <div class="dropdown-item">
<a href="~/Admin/Profiles"><i class=" fa fa-suitcase"></i>个人中心</a> <a href="/Pages/Admin/Profiles"><i class=" fa fa-suitcase"></i>个人中心</a>
<a href="~/Admin/Index"><i class="fa fa-cog"></i>设置</a> <a href="/Pages/Admin/Index"><i class="fa fa-cog"></i>设置</a>
<a href="~/Admin/Notifications"><i class="fa fa-bell"></i>通知<span id="logoutNoti" class="badge badge-pill badge-success"></span></a> <a href="/Pages/Admin/Notifications"><i class="fa fa-bell"></i>通知<span id="logoutNoti" class="badge badge-pill badge-success"></span></a>
</div> </div>
<div class="dropdown-item"> <div class="dropdown-item">
<a href="~/Account/Logout"><i class="fa fa-key"></i>注销</a> <a href="/Account/Logout"><i class="fa fa-key"></i>注销</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"><a href="~/Admin/Index"><i class="fa fa-home"></i>首页</a></li> <li class="breadcrumb-item"><a href="/Pages"><i class="fa fa-home"></i>首页</a></li>
<li class="breadcrumb-item d-none" id="breadNav"></li> <li class="breadcrumb-item d-none" id="breadNav"></li>
</ol> </ol>
</nav> </nav>
</header> </header>
@code { @code {
private HeaderBarModel Model; private HeaderBarModel? Model;
private ClaimsPrincipal User; private ClaimsPrincipal? User;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {

View File

@ -1,14 +1,16 @@
@inherits LayoutComponentBase @inherits LayoutComponentBase
@inject AuthenticationStateProvider authenticationStateProvider
@inject NavigationManager navigationManager
<Header></Header> <Header></Header>
<div class="sidebar"> <div class="sidebar">
<NavMenu /> <NavMenu />
</div> </div>
<section id="main-content" class="main-content @Model.ShowCardTitle"> <section id="main-content" class="main-content @Model?.ShowCardTitle">
<input id="lockScreenPeriod" type="hidden" asp-condition="@Model.EnableAutoLockScreen" value="@Model.LockScreenPeriod" /> <input id="lockScreenPeriod" type="hidden" asp-condition="@Model?.EnableAutoLockScreen" value="@Model?.LockScreenPeriod" />
<div class="main-header"> <div class="main-header">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"><a href="~/Admin/Index"><i class="fa fa-home"></i>首页</a></li> <li class="breadcrumb-item"><a href="/Pages"><i class="fa fa-home"></i>首页</a></li>
<li class="breadcrumb-item d-none"></li> <li class="breadcrumb-item d-none"></li>
</ol> </ol>
</div> </div>
@ -17,11 +19,18 @@
<Footer></Footer> <Footer></Footer>
@code { @code {
private AdminModel Model; private AdminModel? Model;
protected override Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{
var state = await authenticationStateProvider.GetAuthenticationStateAsync();
if (!state.User.Identity.IsAuthenticated)
{
navigationManager.NavigateTo("/Account/Login?returnUrl=" + WebUtility.UrlEncode(new Uri(navigationManager.Uri).PathAndQuery));
}
else
{ {
Model = new AdminModel(); Model = new AdminModel();
return Task.CompletedTask; }
} }
} }

View File

@ -1,42 +1,43 @@
@inject AuthenticationStateProvider authenticationStateProvider @inject AuthenticationStateProvider authenticationStateProvider
@inject NavigationManager navigationManager
<aside class="@Model.ShowSideBar"> <aside class="@Model?.ShowSideBar">
<div class="bg"></div> <div class="bg"></div>
<div class="nav-brand justify-content-center"> <div class="nav-brand justify-content-center">
<a href="#"> <a href="#">
<img src="@Model.WebSiteLogo" /> <img src="@Model?.WebSiteLogo.ToBlazorLink()" />
<span>@Model.Title</span> <span>@Model?.Title</span>
</a> </a>
</div> </div>
<div class="nav-header flex-fill align-items-center"> <div class="nav-header flex-fill align-items-center">
<a href="~/Admin/Profiles"> <a href="/Pages/Admin/Profiles">
<img src="@Model.Icon" /> <img src="@Model?.Icon.ToBlazorLink()" />
<span class="username d-inline-block text-truncate flex-fill">@Model.DisplayName</span> <span class="username d-inline-block text-truncate flex-fill">@Model?.DisplayName</span>
</a> </a>
</div> </div>
<!-- sidebar menu start--> <!-- sidebar menu start-->
<div class="sidebar"> <div class="sidebar">
<ul class="nav-sidebar nav nav-pills flex-column flex-nowrap"> <ul class="nav-sidebar nav nav-pills flex-column flex-nowrap">
@foreach (var menu in Model.Navigations) @if (Model != null)
{
foreach (var menu in Model.Navigations)
{ {
<li class="nav-item" id="@string.Format("menus_{0}",menu.Id)"> <li class="nav-item" id="@string.Format("menus_{0}",menu.Id)">
<a href="@menu.Url" class="nav-link @menu.Active" target="@menu.Target"><i class="@menu.Icon"></i><span class="flex-fill">@menu.Name</span></a> <a href="@menu.Url.ToBlazorMenuUrl()" class="nav-link @menu.Active" target="@menu.Target"><i class="@menu.Icon"></i><span class="flex-fill">@menu.Name</span></a>
</li> </li>
} }
}
</ul> </ul>
</div> </div>
<!-- sidebar menu end--> <!-- sidebar menu end-->
</aside> </aside>
@code { @code {
private NavigatorBarModel Model; private NavigatorBarModel? Model;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
var state = await authenticationStateProvider.GetAuthenticationStateAsync(); var state = await authenticationStateProvider.GetAuthenticationStateAsync();
if (state.User.Identity.IsAuthenticated)
{
Model = new NavigatorBarModel(state.User.Identity.Name); Model = new NavigatorBarModel(state.User.Identity.Name);
} }
}
} }

View File

@ -1,33 +0,0 @@
@inject AuthenticationStateProvider authenticationStateProvider
@inject NavigationManager navigationManager
@if (showNotAuthorizedMessage)
{
<section class="section" id="not-authorized-message">
<div class="container">
<div class="notification is-danger">
</div>
</div>
</section>
}
@code {
private bool showNotAuthorizedMessage = false;
protected override async Task OnInitializedAsync()
{
var state = await authenticationStateProvider.GetAuthenticationStateAsync();
if (!state.User.Identity.IsAuthenticated)
{
// If the user is not authenticated redirect them to the sign in page
navigationManager.NavigateTo("/Account/Login?returnUrl=" + System.Net.WebUtility.UrlEncode(new Uri(navigationManager.Uri).PathAndQuery));
}
else
{
// If the user is signed in, but authorization failed, display a message
showNotAuthorizedMessage = true;
}
}
}

View File

@ -3,7 +3,6 @@ using Longbow.Web.SignalR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
@ -44,13 +43,7 @@ namespace Bootstrap.Admin
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All)); services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
services.Configure<CookiePolicyOptions>(options => services.AddLogging(logging => logging.AddFileLogger().AddCloudLogger().AddDBLogger(ExceptionsHelper.Log));
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddLogging(logging => logging.AddFileLogger().AddDBLogger(ExceptionsHelper.Log));
services.AddCors(); services.AddCors();
services.AddResponseCompression(); services.AddResponseCompression();

View File

@ -147,10 +147,11 @@
<input type="text" class="form-control flex-sm-fill" id="url" placeholder="不可为空4000字以内" maxlength="4000" data-valid="true" /> <input type="text" class="form-control flex-sm-fill" id="url" placeholder="不可为空4000字以内" maxlength="4000" data-valid="true" />
</div> </div>
<div class="form-group col-sm-6"> <div class="form-group col-sm-6">
<label class="control-label" for="category">菜单类别</label> <label class="control-label" for="isRes">菜单类型</label>
<select data-toggle="lgbSelect" class="d-none" data-default-val="1" id="category"> <select data-toggle="lgbSelect" class="d-none menuChild" data-default-val="0" id="isRes" data-valid="true">
<option value="0">系统菜单</option> <option value="0">菜单</option>
<option value="1">外部菜单</option> <option value="1">资源</option>
<option value="2">按钮</option>
</select> </select>
</div> </div>
<div class="form-group col-sm-6"> <div class="form-group col-sm-6">
@ -162,14 +163,6 @@
<option value="_top">顶级窗口</option> <option value="_top">顶级窗口</option>
</select> </select>
</div> </div>
<div class="form-group col-sm-6">
<label class="control-label" for="isRes">菜单类型</label>
<select data-toggle="lgbSelect" class="d-none" data-default-val="0" id="isRes">
<option value="0">菜单</option>
<option value="1">资源</option>
<option value="2">按钮</option>
</select>
</div>
<div class="form-group col-sm-6"> <div class="form-group col-sm-6">
<label class="control-label" for="app">所属应用</label> <label class="control-label" for="app">所属应用</label>
<select data-toggle="lgbSelect" class="d-none" data-default-val="@BootstrapAppContext.AppId" id="app"> <select data-toggle="lgbSelect" class="d-none" data-default-val="@BootstrapAppContext.AppId" id="app">
@ -179,6 +172,13 @@
} }
</select> </select>
</div> </div>
<div class="form-group col-sm-6">
<label class="control-label" for="category">菜单类别</label>
<select data-toggle="lgbSelect" class="d-none" disabled data-default-val="1" id="category">
<option value="0">系统菜单</option>
<option value="1">外部菜单</option>
</select>
</div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -1,50 +1,45 @@
@model TaskModel @model TaskModel
@{ @{
ViewBag.Title = "任务管理"; ViewBag.Title = "任务管理";
Layout = "_Default"; Layout = "_Admin";
} }
@section css { @section css {
<environment include="Development">
<link href="~/lib/bootstrap-table/bootstrap-table.css" rel="stylesheet" />
</environment>
<environment exclude="Development">
<link href="~/lib/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" />
</environment>
<link href="~/lib/longbow-select/longbow-select.css" rel="stylesheet" /> <link href="~/lib/longbow-select/longbow-select.css" rel="stylesheet" />
<link href="~/css/tasks.css" rel="stylesheet" asp-append-version="true" /> <link href="~/css/tasks.css" rel="stylesheet" asp-append-version="true" />
} }
@section javascript { @section javascript {
<environment include="Development">
<script src="~/lib/bootstrap-table/bootstrap-table.js"></script>
<script src="~/lib/bootstrap-table/extensions/export/bootstrap-table-export.js"></script>
<script src="~/lib/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script>
<script src="~/lib/tablexport/tableExport.js"></script>
<script src="~/lib/validate/jquery.validate.js"></script>
<script src="~/lib/validate/localization/messages_zh.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/bootstrap-table/bootstrap-table.min.js"></script>
<script src="~/lib/bootstrap-table/extensions/export/bootstrap-table-export.min.js"></script>
<script src="~/lib/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
<script src="~/lib/tablexport/tableExport.min.js"></script>
<script src="~/lib/validate/jquery.validate.min.js"></script>
<script src="~/lib/validate/localization/messages_zh.min.js"></script>
</environment>
<script src="~/lib/longbow-checkbox/longbow-checkbox.js"></script>
<script src="~/lib/longbow-select/longbow-select.js"></script> <script src="~/lib/longbow-select/longbow-select.js"></script>
<script src="~/lib/longbow/longbow.dataentity.js" asp-append-version="true"></script>
<script src="~/lib/longbow/longbow.validate.js" asp-append-version="true"></script>
<script src="~/js/tasks.js" asp-append-version="true"></script> <script src="~/js/tasks.js" asp-append-version="true"></script>
} }
@section tableButtons {
<button class='info btn btn-sm btn-info' asp-auth="info"><i class='fa fa-info'></i><span>日志</span></button>
}
@section cardbody {
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止修改定时后台任务</span>
</div>
}
@section query {
<div class="alert alert-info">
<p class="font-weight-bold">后台任务说明:</p>
<p>1. 默认任务 (立即执行,仅执行一次)</p>
<p>
<code>
TaskServicesManager.GetOrAdd("简单任务", token => Task.Delay(1000));
</code>
</p>
<p>2. 周期性任务 (1 分钟后间隔 5 秒执行 2 次任务)</p>
<p>
<code>
var trigger = TriggerBuilder.Default.WithInterval(TimeSpan.FromSeconds(5)).WithRepeatCount(2).WithStartTime(DateTimeOffset.Now.AddMinutes(1)).Build();
<br />
TaskServicesManager.GetOrAdd("测试任务", token => Task.Delay(1000), trigger);
</code>
</p>
<p>3. Cron 表达式任务 (间隔 5 秒循环执行任务)</p>
<p>
<code>
TaskServicesManager.GetOrAdd("Cron 表达式任务", token => Task.Delay(1000), TriggerBuilder.Build("*/5 * * * * *"));
</code>
</p>
</div>
}
@section modal { @section modal {
<div class="modal fade" id="dialogNew" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content" data-toggle="LgbValidate" data-valid-button="#btnSubmit" data-valid-modal="#dialogNew">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="myModalLabel">任务编辑窗口</h5> <h5 class="modal-title" id="myModalLabel">任务编辑窗口</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
@ -73,8 +68,19 @@
</div> </div>
</form> </form>
</div> </div>
} <div class="modal-footer">
@section customModal { <button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fa fa-times"></i>
<span>关闭</span>
</button>
<button type="button" class="btn btn-primary" id="btnSubmit">
<i class="fa fa-save"></i>
<span>保存</span>
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="dialogLog" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="taskModalLabel" aria-hidden="true"> <div class="modal fade" id="dialogLog" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="taskModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document"> <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
@ -98,3 +104,49 @@
</div> </div>
</div> </div>
} }
<div class="card">
<div class="card-header">任务介绍</div>
<div class="card-body">
<div class="alert alert-info">
<p class="font-weight-bold">后台任务说明:</p>
<p>1. 默认任务 (立即执行,仅执行一次)</p>
<p>
<code>
TaskServicesManager.GetOrAdd("简单任务", token => Task.Delay(1000));
</code>
</p>
<p>2. 周期性任务 (1 分钟后间隔 5 秒执行 2 次任务)</p>
<p>
<code>
var trigger = TriggerBuilder.Default.WithInterval(TimeSpan.FromSeconds(5)).WithRepeatCount(2).WithStartTime(DateTimeOffset.Now.AddMinutes(1)).Build();
<br />
TaskServicesManager.GetOrAdd("测试任务", token => Task.Delay(1000), trigger);
</code>
</p>
<p>3. Cron 表达式任务 (间隔 5 秒循环执行任务)</p>
<div>
<code>
TaskServicesManager.GetOrAdd("Cron 表达式任务", token => Task.Delay(1000), TriggerBuilder.Build("*/5 * * * * *"));
</code>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
查询结果
</div>
<div class="card-body">
<div class="alert alert-danger" role="alert" asp-condition="@Model.IsDemo">
<span>演示系统禁止修改定时后台任务</span>
</div>
<table></table>
</div>
</div>
<div id="tableButtons" class="d-none">
<div class='btn-group'>
<button class='pause btn btn-sm btn-danger' asp-auth="pause"><i class='fa fa-pause-circle'></i><span>暂停</span></button>
<button class='run btn btn-sm btn-success' asp-auth="pause"><i class='fa fa-play-circle'></i><span>运行</span></button>
<button class='info btn btn-sm btn-info' asp-auth="info"><i class='fa fa-info-circle'></i><span>日志</span></button>
</div>
</div>

View File

@ -1,4 +1,5 @@
@using System.Net.Http @using System.Net
@using System.Net.Http
@using System.Security.Claims @using System.Security.Claims
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Authorization
@ -6,7 +7,9 @@
@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Mvc @using Microsoft.AspNetCore.Mvc
@using Microsoft.Extensions.Configuration
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using Bootstrap.Admin @using Bootstrap.Admin
@using Bootstrap.Admin.Shared
@using Bootstrap.Admin.Models @using Bootstrap.Admin.Models
@using Bootstrap.Admin.Pages
@using Bootstrap.Admin.Shared

View File

@ -13,8 +13,15 @@
}, },
"FileName": "Error\\Log.log", "FileName": "Error\\Log.log",
"MaxFileCount": 1 "MaxFileCount": 1
},
"Cloud": {
"LogLevel": {
"Default": "Error"
},
"Url": "https://client.sdgxgz.com/api/Interface/Log"
} }
}, },
"AutoGenerateDatabase": true,
"DB": [ "DB": [
{ {
"Enabled": false, "Enabled": false,
@ -60,6 +67,7 @@
"UseBlazor": true, "UseBlazor": true,
"SwaggerPathBase": "", "SwaggerPathBase": "",
"AllowOrigins": "http://localhost:49185", "AllowOrigins": "http://localhost:49185",
"HealthsCloudUrl": "https://client.sdgxgz.com/api/Interface/Healths",
"FileStorageOptions": { "FileStorageOptions": {
"Enabled": true, "Enabled": true,
"Folder": "TaskStorage", "Folder": "TaskStorage",

View File

@ -10,11 +10,18 @@
"Default": "Error" "Default": "Error"
}, },
"FileName": "Error\\Log.log" "FileName": "Error\\Log.log"
},
"Cloud": {
"LogLevel": {
"Default": "Error"
},
"Url": "https://client.sdgxgz.com/api/Interface/Log"
} }
}, },
"ConnectionStrings": { "ConnectionStrings": {
"ba": "Data Source=.;Initial Catalog=BootstrapAdmin;User ID=sa;Password=sa" "ba": "Data Source=.;Initial Catalog=BootstrapAdmin;User ID=sa;Password=sa"
}, },
"AutoGenerateDatabase": false,
"DB": [ "DB": [
{ {
"Enabled": false, "Enabled": false,
@ -54,8 +61,10 @@
} }
], ],
"AppId": "BA", "AppId": "BA",
"UseBlazor": true,
"SwaggerPathBase": "", "SwaggerPathBase": "",
"AllowOrigins": "http://localhost,http://ba.sdgxgz.cn", "AllowOrigins": "http://localhost,http://ba.sdgxgz.cn",
"HealthsCloudUrl": "https://client.sdgxgz.com/api/Interface/Healths",
"GiteeHealthChecks": false, "GiteeHealthChecks": false,
"Sentry": { "Sentry": {
"Dsn": "https://70bdfff562e84fa7b9a43d65924ab9ad@sentry.io/1469396" "Dsn": "https://70bdfff562e84fa7b9a43d65924ab9ad@sentry.io/1469396"

View File

@ -353,6 +353,10 @@ body.trans-mute * {
right: 45px; right: 45px;
} }
li.is-disabled .dd3-content {
color: #c0c4cc;
}
@media (max-width: 480px) { @media (max-width: 480px) {
.dd3-content .menuType { .dd3-content .menuType {
display: none; display: none;

View File

@ -1,4 +1,9 @@
(function ($) { (function ($) {
$.extend({
sendHealths: function (data) {
$.bc({ url: 'api/Interface/Healths', data: JSON.stringify(data), method: 'post' });
}
});
$.fn.extend({ $.fn.extend({
autoScrollSidebar: function (options) { autoScrollSidebar: function (options) {
var option = $.extend({ target: null, offsetTop: 0 }, options); var option = $.extend({ target: null, offsetTop: 0 }, options);
@ -75,7 +80,7 @@
var resultFormat = { var resultFormat = {
"Success": '<span class="badge badge-pill badge-success badge-task"><i class="fa fa-check-circle"></i><span>成功</span></span>', "Success": '<span class="badge badge-pill badge-success badge-task"><i class="fa fa-check-circle"></i><span>成功</span></span>',
"Timeout": '<span class="badge badge-pill badge-warning badge-task"><i class="fa fa-exclamation-circle"></i><span>超时</span></span>' "Timeout": '<span class="badge badge-pill badge-warning badge-task"><i class="fa fa-exclamation-circle"></i><span>超时</span></span>'
} };
var htmlUserTemplate = '<a class="dropdown-item position-relative" href="{0}"><span class="label label-primary"><i class="fa fa-thumb-tack"></i></span><div class="content">{1}</div><div class="small italic content-task">{2}</div>{3}</a>'; var htmlUserTemplate = '<a class="dropdown-item position-relative" href="{0}"><span class="label label-primary"><i class="fa fa-thumb-tack"></i></span><div class="content">{1}</div><div class="small italic content-task">{2}</div>{3}</a>';
var html = result.Tasks.map(function (u) { var html = result.Tasks.map(function (u) {
return $.format(htmlUserTemplate, $.formatUrl('Admin/Tasks'), u.Name, u.LastRuntime, resultFormat[u.LastRunResult]); return $.format(htmlUserTemplate, $.formatUrl('Admin/Tasks'), u.Name, u.LastRuntime, resultFormat[u.LastRunResult]);
@ -131,10 +136,10 @@
var html = ""; var html = "";
$.each(menus, function (index, menu) { $.each(menus, function (index, menu) {
if (menu.Menus.length === 0) { if (menu.Menus.length === 0) {
html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{4}" data-category="{3}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{5}</span><span class="menuOrder">{4}</span></div></li>', menu.Id, menu.Icon, menu.Name, menu.Category, menu.Order, formatCategoryName(menu)); html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{4}" data-category="{3}" data-resource="{6}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{5}</span><span class="menuOrder">{4}</span></div></li>', menu.Id, menu.Icon, menu.Name, menu.Category, menu.Order, formatCategoryName(menu), menu.IsResource);
} }
else { else {
html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{5}" data-category="{3}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{6}</span><span class="menuOrder">{5}</span></div><ol class="dd-list">{4}</ol></li>', menu.Id, menu.Icon, menu.Name, menu.Category, cascadeSubMenu(menu.Menus), menu.Order, formatCategoryName(menu)); html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{5}" data-category="{3}" data-resource="{7}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{6}</span><span class="menuOrder">{5}</span></div><ol class="dd-list">{4}</ol></li>', menu.Id, menu.Icon, menu.Name, menu.Category, cascadeSubMenu(menu.Menus), menu.Order, formatCategoryName(menu), menu.IsResource);
} }
}); });
return html; return html;
@ -144,10 +149,10 @@
var html = ""; var html = "";
$.each(menus, function (index, menu) { $.each(menus, function (index, menu) {
if (menu.Menus.length === 0) { if (menu.Menus.length === 0) {
html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{4}" data-category="{3}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{5}</span><span class="menuOrder">{4}</span></div></li>', menu.Id, menu.Icon, menu.Name, menu.Category, menu.Order, formatCategoryName(menu)); html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{4}" data-category="{3}" data-resource="{6}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{5}</span><span class="menuOrder">{4}</span></div></li>', menu.Id, menu.Icon, menu.Name, menu.Category, menu.Order, formatCategoryName(menu), menu.IsResource);
} }
else { else {
html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{5}" data-category="{3}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{6}</span><span class="menuOrder">{5}</span></div><ol class="dd-list">{4}</ol></li>', menu.Id, menu.Icon, menu.Name, menu.Category, cascadeSubMenu(menu.Menus), menu.Order, formatCategoryName(menu)); html += $.format('<li class="dd-item dd3-item" data-id="{0}" data-order="{5}" data-category="{3}" data-resource="{7}"><div class="dd-handle dd3-handle"></div><div class="dd3-content"><div class="checkbox"><label><input type="checkbox" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><div class="radio"><label><input type="radio" name="menu" value="{0}"><span><i class="{1}"></i>{2}</span></label></div><span class="menuType">{6}</span><span class="menuOrder">{5}</span></div><ol class="dd-list">{4}</ol></li>', menu.Id, menu.Icon, menu.Name, menu.Category, cascadeSubMenu(menu.Menus), menu.Order, formatCategoryName(menu), menu.IsResource);
} }
}); });
return html; return html;

View File

@ -49,6 +49,8 @@
$.bc({ $.bc({
url: 'healths', url: 'healths',
callback: function (result) { callback: function (result) {
// async send result to cloud
$.sendHealths(result);
var data = $.map(result.Keys, function (name) { var data = $.map(result.Keys, function (name) {
return { Name: name, Duration: result.Report.Entries[name].Duration, Status: result.Report.Entries[name].Status, Exception: result.Report.Entries[name].Exception, Description: result.Report.Entries[name].Description, Data: result.Report.Entries[name].Data }; return { Name: name, Duration: result.Report.Entries[name].Duration, Status: result.Report.Entries[name].Status, Exception: result.Report.Entries[name].Exception, Description: result.Report.Entries[name].Description, Data: result.Report.Entries[name].Data };
}); });

View File

@ -217,6 +217,7 @@ $(function () {
$btnSubmitMenu.data('type', 'parent'); $btnSubmitMenu.data('type', 'parent');
$nestMenuInput.find('label:last').find('input').show(); $nestMenuInput.find('label:last').find('input').show();
$nestMenu.find('li.dd-item').hide().remove('[data-id="0"]'); $nestMenu.find('li.dd-item').hide().remove('[data-id="0"]');
$nestMenu.find('li[data-resource!="0"]').addClass('is-disabled').find(':radio').prop('disabled', true)
$nestMenu.find('li[data-category="' + $category.val() + '"]').show(); $nestMenu.find('li[data-category="' + $category.val() + '"]').show();
showDialog(); showDialog();
}); });
@ -240,8 +241,16 @@ $(function () {
var type = $(this).data('type'); var type = $(this).data('type');
switch (type) { switch (type) {
case "parent": case "parent":
$parentMenuID.val($('.dd3-content :radio:checked').val()); // 父级菜单不可以是资源或者按钮类型
var pId = $('.dd3-content :radio:checked').val();
var check = $.remoteValidate('api/Category/ValidateParentMenuById/' + pId);
if (check) {
$parentMenuID.val(pId);
$parentMenuName.val($('.dd3-content :radio:checked').next('span').text()); $parentMenuName.val($('.dd3-content :radio:checked').next('span').text());
}
else {
return false;
}
break; break;
case "order": case "order":
var data = $nestMenu.find('li:visible'); var data = $nestMenu.find('li:visible');
@ -333,4 +342,19 @@ $(function () {
}); });
} }
}); });
// 所属应用更新是联动菜单类别
var $app = $('#app').on('changed.lgbSelect', function (e) {
var defaultVal = $app.attr('data-default-val');
var val = defaultVal === $app.val() ? '0' : '1';
$category.lgbSelect('val', val);
})
if ($.isFunction($.validator)) {
$.validator.addMethod("menuChild", function (value, element) {
var id = $("#menuID").val();
var check = id === "" || value === "菜单" || $.remoteValidate('api/Category/ValidateMenuBySubMenu/' + id);
return check;
}, "拥有子菜单时菜单类型不可更改为资源或者按钮");
}
}); });

View File

@ -11,7 +11,7 @@
content = $.format(template, 'success', 'play-circle', '运行中'); content = $.format(template, 'success', 'play-circle', '运行中');
} }
else if (value === "Disabled") { else if (value === "Disabled") {
content = $.format(template, 'danger', 'times-circle', '已禁用'); content = $.format(template, 'danger', 'times-circle', '已停止');
} }
return content; return content;
}; };
@ -33,7 +33,7 @@
return content; return content;
}; };
$('.card-body table').lgbTable({ var $task = $('.card-body table').lgbTable({
url: Tasks.url, url: Tasks.url,
dataBinder: { dataBinder: {
map: { map: {
@ -43,8 +43,8 @@
}, },
smartTable: { smartTable: {
sidePagination: "client", sidePagination: "client",
sortName: 'CreateTime', sortName: 'Name',
sortOrder: 'desc', sortOrder: 'asc',
queryParams: function (params) { return $.extend(params, { operateType: $("#txt_operate_type").val(), OperateTimeStart: $("#txt_operate_start").val(), OperateTimeEnd: $("#txt_operate_end").val() }); }, queryParams: function (params) { return $.extend(params, { operateType: $("#txt_operate_type").val(), OperateTimeStart: $("#txt_operate_start").val(), OperateTimeEnd: $("#txt_operate_end").val() }); },
columns: [ columns: [
{ title: "名称", field: "Name", sortable: true }, { title: "名称", field: "Name", sortable: true },
@ -95,6 +95,22 @@
console.log(error); console.log(error);
} }
}); });
},
'click .pause': function (e, value, row, index) {
$.bc({
url: 'api/Tasks/' + row.Name + "?operType=" + "pause", method: 'put',
callback: function (result) {
if (result) $task.bootstrapTable('refresh');
}
});
},
'click .run': function (e, value, row, index) {
$.bc({
url: 'api/Tasks/' + row.Name + "?operType=" + "run", method: 'put',
callback: function (result) {
if (result) $task.bootstrapTable('refresh');
}
});
} }
} }
} }

View File

@ -50,6 +50,10 @@
cursor: not-allowed; cursor: not-allowed;
} }
.form-select.is-disabled .form-select-append {
color: #c0c4cc;
}
.form-select-input { .form-select-input {
color: #606266; color: #606266;
outline: none; outline: none;

View File

@ -114,11 +114,17 @@
// save attributes // save attributes
var attrs = []; var attrs = [];
["id", "data-default-val"].forEach(function (v, index) { ["id", "name", "class", "data-valid", "data-default-val"].forEach(function (v, index) {
var value = that.$element.attr(v); var value = that.$element.attr(v);
if (value !== undefined) attrs.push({ name: v, value: value }); if (value !== undefined) attrs.push({ name: v, value: value });
}); });
var disabled = this.$element.prop('disabled');
// set disabled property
if (disabled) {
this.disabled();
}
// replace element select -> input hidden // replace element select -> input hidden
this.$element.remove(); this.$element.remove();
this.$element = $('<input type="hidden" data-toggle="lgbSelect" />').val(that.val()).insertBefore(this.$input); this.$element = $('<input type="hidden" data-toggle="lgbSelect" />').val(that.val()).insertBefore(this.$input);
@ -139,12 +145,15 @@
this.$input.removeAttr('disabled'); this.$input.removeAttr('disabled');
}; };
_proto.reset = function (value) { _proto.reset = function (value, init) {
var that = this; var that = this;
// keep old value // keep old value
var oldValue = this.$input.attr('value'); var oldValue = this.$input.attr('value');
// default select value
if (init == undefined) init = true;
// warning: must use attr('value') method instead of val(). otherwise the others input html element will filled by first element value. // warning: must use attr('value') method instead of val(). otherwise the others input html element will filled by first element value.
// see https://gitee.com/LongbowEnterprise/longbow-select/issues/IZ3BR?from=project-issue // see https://gitee.com/LongbowEnterprise/longbow-select/issues/IZ3BR?from=project-issue
this.$input.attr('value', '').removeClass('is-valid is-invalid'); this.$input.attr('value', '').removeClass('is-valid is-invalid');
@ -153,11 +162,13 @@
$.each(value, function (index) { $.each(value, function (index) {
var $item = $('<a class="dropdown-item" href="#" data-val="' + this.value + '">' + this.text + '</a>'); var $item = $('<a class="dropdown-item" href="#" data-val="' + this.value + '">' + this.text + '</a>');
that.$menus.append($item); that.$menus.append($item);
if (init) {
if (this.selected === true || this.value === oldValue || index === 0 || this.value === that.$element.attr('data-default-val')) { if (this.selected === true || this.value === oldValue || index === 0 || this.value === that.$element.attr('data-default-val')) {
that.$input.attr('value', this.text); that.$input.attr('value', this.text);
that.$element.val(this.value).attr('data-text', this.text); that.$element.val(this.value).attr('data-text', this.text);
$activeItem = $item; $activeItem = $item;
} }
}
}); });
if ($activeItem !== null) $activeItem.addClass('active'); if ($activeItem !== null) $activeItem.addClass('active');

View File

@ -125,6 +125,19 @@
document.webkitIsFullScreen || window.fullscreen || document.webkitIsFullScreen || window.fullscreen ||
false; false;
}, },
remoteValidate: function (url, method) {
if (method === undefined) method = 'get';
var check = false;
jQuery[method]({
url: $.formatUrl(url),
async: false,
cache: false,
success: function (result) {
check = result
}
});
return check;
},
bc: function (options) { bc: function (options) {
options = $.extend({ options = $.extend({
id: "", id: "",

View File

@ -68,9 +68,13 @@
} }
else if (dv !== undefined && ctl.val() === "") target[name] = dv; else if (dv !== undefined && ctl.val() === "") target[name] = dv;
else target[name] = ctl.val(); else target[name] = ctl.val();
// check boolean value
if (ctl.attr('data-bool') === 'true') {
if (target[name] === "true" || target[name] === "True") target[name] = true; if (target[name] === "true" || target[name] === "True") target[name] = true;
if (target[name] === "false" || target[name] === "False") target[name] = false; if (target[name] === "false" || target[name] === "False") target[name] = false;
} }
}
return target; return target;
} }
}; };
@ -183,7 +187,7 @@
callback: function (result) { callback: function (result) {
if (result) { if (result) {
$(options.bootstrapTable).bootstrapTable('refresh'); $(options.bootstrapTable).bootstrapTable('refresh');
handlerCallback.call(that, null, element, { oper: 'save', success: result, data: options.data }); handlerCallback.call(that, null, element, { oper: 'save', success: result, data: [options.data] });
} }
} }
}); });

View File

@ -22,8 +22,8 @@ namespace Bootstrap.DataAccess.MongoDB
Id = d.Key, Id = d.Key,
AppName = d.Value AppName = d.Value
}).ToList(); }).ToList();
var role = RoleHelper.Retrieves().Cast<Role>().FirstOrDefault(r => r.Id == roleId); var role = RoleHelper.Retrieves().Cast<Role>().FirstOrDefault(r => r.Id == roleId) ?? new Role();
apps.ForEach(p => p.Checked = (role != null && (role.Apps.Contains(p.Id)) || role.RoleName.Equals("Administrators", StringComparison.OrdinalIgnoreCase)) ? "checked" : ""); apps.ForEach(p => p.Checked = (role.Apps.Contains(p.Id) || role.RoleName.Equals("Administrators", StringComparison.OrdinalIgnoreCase)) ? "checked" : "");
return apps; return apps;
} }

View File

@ -1,4 +1,7 @@
namespace Bootstrap.DataAccess.MongoDB using Bootstrap.Security;
using MongoDB.Driver;
namespace Bootstrap.DataAccess.MongoDB
{ {
/// <summary> /// <summary>
/// 自动建库实体操作类 /// 自动建库实体操作类
@ -9,9 +12,10 @@
/// 数据库检查方法 /// 数据库检查方法
/// <paramref name="folder"></paramref> /// <paramref name="folder"></paramref>
/// </summary> /// </summary>
public override void CheckDB(string folder) public override void EnsureCreated(string folder)
{ {
// UNDONE: 没有环境暂时未写代码 // 检查数据库是否存在
if (DbManager.Dicts.CountDocuments(FilterDefinition<BootstrapDict>.Empty) == 0) GenerateDB(folder);
} }
} }
} }

View File

@ -5,8 +5,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bootstrap.Security" Version="3.0.0" /> <PackageReference Include="Bootstrap.Security" Version="3.0.1" />
<PackageReference Include="MongoDB.Driver" Version="2.9.2" /> <PackageReference Include="MongoDB.Driver" Version="2.9.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -13,7 +13,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// </summary> /// </summary>
internal static class DbManager internal static class DbManager
{ {
private static IMongoDatabase _db = null; private static IMongoDatabase? _db = null;
private static bool _register = false; private static bool _register = false;
private static readonly object _locker = new object(); private static readonly object _locker = new object();
@ -38,7 +38,9 @@ namespace Bootstrap.DataAccess.MongoDB
InitDb(); InitDb();
} }
} }
#pragma warning disable CS8603 // 可能的 null 引用返回。
return _db; return _db;
#pragma warning restore CS8603 // 可能的 null 引用返回。
} }
} }

View File

@ -41,7 +41,6 @@ namespace Bootstrap.DataAccess.MongoDB
{ {
if (string.IsNullOrEmpty(p.Id)) if (string.IsNullOrEmpty(p.Id))
{ {
p.Id = null;
DbManager.Dicts.InsertOne(p); DbManager.Dicts.InsertOne(p);
p.Id = DbManager.Dicts.Find(d => d.Name == p.Name && d.Category == p.Category && d.Define == p.Define && d.Code == p.Code).FirstOrDefault().Id; p.Id = DbManager.Dicts.Find(d => d.Name == p.Name && d.Category == p.Category && d.Define == p.Define && d.Code == p.Code).FirstOrDefault().Id;
} }

View File

@ -1,4 +1,4 @@
using Longbow.Web.Mvc; using Longbow.Web.Mvc;
using MongoDB.Driver; using MongoDB.Driver;
using PetaPoco; using PetaPoco;
using System; using System;
@ -75,12 +75,9 @@ namespace Bootstrap.DataAccess.MongoDB
// sort // sort
var sortBuilder = Builders<DataAccess.Exceptions>.Sort; var sortBuilder = Builders<DataAccess.Exceptions>.Sort;
SortDefinition<DataAccess.Exceptions> sort = null; var sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.LogTime) : sortBuilder.Descending(t => t.LogTime);
switch (po.Sort) switch (po.Sort)
{ {
case "LogTime":
sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.LogTime) : sortBuilder.Descending(t => t.LogTime);
break;
case "ErrorPage": case "ErrorPage":
sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.ErrorPage) : sortBuilder.Descending(t => t.ErrorPage); sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.ErrorPage) : sortBuilder.Descending(t => t.ErrorPage);
break; break;

View File

@ -13,7 +13,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public IEnumerable<string> Roles { get; set; } public IEnumerable<string> Roles { get; set; } = new List<string>();
/// <summary> /// <summary>
/// ///

View File

@ -20,7 +20,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// <param name="endTime"></param> /// <param name="endTime"></param>
/// <param name="opType"></param> /// <param name="opType"></param>
/// <returns></returns> /// <returns></returns>
public override Page<DataAccess.Log> RetrievePages(PaginationOption po, DateTime? startTime, DateTime? endTime, string opType) public override Page<DataAccess.Log> RetrievePages(PaginationOption po, DateTime? startTime, DateTime? endTime, string? opType)
{ {
var filterBuilder = Builders<DataAccess.Log>.Filter; var filterBuilder = Builders<DataAccess.Log>.Filter;
var filter = filterBuilder.Empty; var filter = filterBuilder.Empty;
@ -31,7 +31,7 @@ namespace Bootstrap.DataAccess.MongoDB
// sort // sort
var sortBuilder = Builders<DataAccess.Log>.Sort; var sortBuilder = Builders<DataAccess.Log>.Sort;
SortDefinition<DataAccess.Log> sort = null; var sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.LogTime) : sortBuilder.Descending(t => t.LogTime);
switch (po.Sort) switch (po.Sort)
{ {
case "CRUD": case "CRUD":
@ -40,9 +40,6 @@ namespace Bootstrap.DataAccess.MongoDB
case "UserName": case "UserName":
sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.UserName) : sortBuilder.Descending(t => t.UserName); sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.UserName) : sortBuilder.Descending(t => t.UserName);
break; break;
case "LogTime":
sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.LogTime) : sortBuilder.Descending(t => t.LogTime);
break;
case "Ip": case "Ip":
sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.Ip) : sortBuilder.Descending(t => t.Ip); sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.Ip) : sortBuilder.Descending(t => t.Ip);
break; break;
@ -70,7 +67,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// <param name="endTime"></param> /// <param name="endTime"></param>
/// <param name="opType"></param> /// <param name="opType"></param>
/// <returns></returns> /// <returns></returns>
public override IEnumerable<DataAccess.Log> RetrieveAll(DateTime? startTime, DateTime? endTime, string opType) public override IEnumerable<DataAccess.Log> RetrieveAll(DateTime? startTime, DateTime? endTime, string? opType)
{ {
var filterBuilder = Builders<DataAccess.Log>.Filter; var filterBuilder = Builders<DataAccess.Log>.Filter;
var filter = filterBuilder.Empty; var filter = filterBuilder.Empty;

View File

@ -31,7 +31,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// <param name="endTime"></param> /// <param name="endTime"></param>
/// <param name="ip"></param> /// <param name="ip"></param>
/// <returns></returns> /// <returns></returns>
public override Page<DataAccess.LoginUser> RetrieveByPages(PaginationOption po, DateTime? startTime, DateTime? endTime, string ip) public override Page<DataAccess.LoginUser> RetrieveByPages(PaginationOption po, DateTime? startTime, DateTime? endTime, string? ip)
{ {
var logs = RetrieveAll(startTime, endTime, ip); var logs = RetrieveAll(startTime, endTime, ip);
return new Page<DataAccess.LoginUser>() return new Page<DataAccess.LoginUser>()
@ -49,7 +49,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// 获取所有登录数据 /// 获取所有登录数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public override IEnumerable<DataAccess.LoginUser> RetrieveAll(DateTime? startTime, DateTime? endTime, string ip) public override IEnumerable<DataAccess.LoginUser> RetrieveAll(DateTime? startTime, DateTime? endTime, string? ip)
{ {
var filterBuilder = Builders<DataAccess.LoginUser>.Filter; var filterBuilder = Builders<DataAccess.LoginUser>.Filter;
var filter = filterBuilder.Empty; var filter = filterBuilder.Empty;

View File

@ -40,8 +40,8 @@ namespace Bootstrap.DataAccess.MongoDB
var dicts = DictHelper.RetrieveDicts().Where(m => m.Category == "菜单"); var dicts = DictHelper.RetrieveDicts().Where(m => m.Category == "菜单");
menus.ForEach(m => menus.ForEach(m =>
{ {
m.CategoryName = dicts.FirstOrDefault(d => d.Code == m.Category)?.Name; m.CategoryName = dicts.FirstOrDefault(d => d.Code == m.Category)?.Name ?? "";
if (m.ParentId != "0") m.ParentName = menus.FirstOrDefault(p => p.Id == m.ParentId)?.Name; if (m.ParentId != "0") m.ParentName = menus.FirstOrDefault(p => p.Id == m.ParentId)?.Name ?? "";
}); });
return menus; return menus;
@ -56,7 +56,6 @@ namespace Bootstrap.DataAccess.MongoDB
{ {
if (string.IsNullOrEmpty(p.Id)) if (string.IsNullOrEmpty(p.Id))
{ {
p.Id = null;
DbManager.Menus.InsertOne(p); DbManager.Menus.InsertOne(p);
p.Id = DbManager.Menus.Find(m => m.Name == p.Name && m.Category == p.Category && m.ParentId == p.ParentId && m.Url == p.Url).FirstOrDefault().Id; p.Id = DbManager.Menus.Find(m => m.Name == p.Name && m.Category == p.Category && m.ParentId == p.ParentId && m.Url == p.Url).FirstOrDefault().Id;
} }

View File

@ -8,19 +8,18 @@ namespace Bootstrap.DataAccess.MongoDB
public class RejectUser public class RejectUser
{ {
/// <summary> /// <summary>
/// 获得/设置 操作日志主键ID ///
/// </summary> /// </summary>
public string Id { get; set; } public string? Id { get; set; }
/// <summary>
///
/// </summary>
public string UserName { get; set; } = "";
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string UserName { get; set; } public string DisplayName { get; set; } = "";
/// <summary>
///
/// </summary>
public string DisplayName { get; set; }
/// <summary> /// <summary>
/// ///
@ -30,7 +29,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string RejectedBy { get; set; } public string RejectedBy { get; set; } = "";
/// <summary> /// <summary>
/// ///
@ -40,6 +39,6 @@ namespace Bootstrap.DataAccess.MongoDB
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string RejectedReason { get; set; } public string RejectedReason { get; set; } = "";
} }
} }

View File

@ -13,12 +13,12 @@ namespace Bootstrap.DataAccess.MongoDB
/// <summary> /// <summary>
/// 此角色关联的所有菜单 /// 此角色关联的所有菜单
/// </summary> /// </summary>
public IEnumerable<string> Menus { get; set; } public IEnumerable<string> Menus { get; set; } = new List<string>();
/// <summary> /// <summary>
/// 此角色关联的所有应用程序 /// 此角色关联的所有应用程序
/// </summary> /// </summary>
public IEnumerable<string> Apps { get; set; } public IEnumerable<string> Apps { get; set; } = new List<string>();
/// <summary> /// <summary>
/// ///
@ -41,9 +41,7 @@ namespace Bootstrap.DataAccess.MongoDB
DbManager.Roles.InsertOne(new Role() DbManager.Roles.InsertOne(new Role()
{ {
RoleName = p.RoleName, RoleName = p.RoleName,
Description = p.Description, Description = p.Description
Menus = new List<string>(),
Apps = new List<string>()
}); });
p.Id = DbManager.Roles.Find(r => r.RoleName == p.RoleName && r.Description == p.Description).FirstOrDefault().Id; p.Id = DbManager.Roles.Find(r => r.RoleName == p.RoleName && r.Description == p.Description).FirstOrDefault().Id;
} }
@ -130,7 +128,7 @@ namespace Bootstrap.DataAccess.MongoDB
{ {
var roles = RoleHelper.Retrieves().Cast<Role>().ToList(); var roles = RoleHelper.Retrieves().Cast<Role>().ToList();
roles.ForEach(r => r.Checked = (r.Menus != null && r.Menus.Contains(menuId)) ? "checked" : ""); roles.ForEach(r => r.Checked = (r.Menus != null && r.Menus.Contains(menuId)) ? "checked" : "");
roles.ForEach(r => r.Menus = null); roles.ForEach(r => r.Menus = new List<string>());
return roles; return roles;
} }
@ -142,26 +140,41 @@ namespace Bootstrap.DataAccess.MongoDB
/// <returns></returns> /// <returns></returns>
public override bool SavaByMenuId(string menuId, IEnumerable<string> roleIds) public override bool SavaByMenuId(string menuId, IEnumerable<string> roleIds)
{ {
// 参数 id 可能是子菜单
// https://gitee.com/LongbowEnterprise/dashboard/issues?id=IQW93
if (!string.IsNullOrEmpty(menuId))
{
// 找到所有包含此菜单的角色集合
var roles = DbManager.Roles.Find(md => md.Menus != null && md.Menus.Contains(menuId)).ToList(); var roles = DbManager.Roles.Find(md => md.Menus != null && md.Menus.Contains(menuId)).ToList();
// Remove roles // 所有角色集合移除此菜单
roles.ForEach(p => roles.ForEach(p =>
{ {
var menus = p.Menus == null ? new List<string>() : p.Menus.ToList(); var menus = p.Menus.ToList();
menus.Remove(menuId); menus.Remove(menuId);
DbManager.Roles.UpdateOne(md => md.Id == p.Id, Builders<Role>.Update.Set(md => md.Menus, menus)); DbManager.Roles.UpdateOne(md => md.Id == p.Id, Builders<Role>.Update.Set(md => md.Menus, menus));
}); });
// 授权角色集合
string? parentId = menuId;
roles = DbManager.Roles.Find(md => roleIds.Contains(md.Id)).ToList(); roles = DbManager.Roles.Find(md => roleIds.Contains(md.Id)).ToList();
do
{
roles.ForEach(role => roles.ForEach(role =>
{ {
var menus = role.Menus == null ? new List<string>() : role.Menus.ToList(); var menus = role.Menus.ToList();
if (!menus.Contains(menuId)) if (!menus.Contains(parentId))
{ {
menus.Add(menuId); menus.Add(parentId);
DbManager.Roles.UpdateOne(md => md.Id == role.Id, Builders<Role>.Update.Set(md => md.Menus, menus)); DbManager.Roles.UpdateOne(md => md.Id == role.Id, Builders<Role>.Update.Set(md => md.Menus, menus));
} }
}); });
// 查找父级菜单
parentId = DbManager.Menus.Find(md => md.Id == parentId).FirstOrDefault()?.ParentId;
}
while (!string.IsNullOrEmpty(parentId) && parentId != "0");
}
return true; return true;
} }

View File

@ -16,7 +16,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// ///
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public override Page<DataAccess.Trace> RetrievePages(PaginationOption po, DateTime? startTime, DateTime? endTime, string ip) public override Page<DataAccess.Trace> RetrievePages(PaginationOption po, DateTime? startTime, DateTime? endTime, string? ip)
{ {
// filter // filter
var filterBuilder = Builders<DataAccess.Trace>.Filter; var filterBuilder = Builders<DataAccess.Trace>.Filter;
@ -28,12 +28,9 @@ namespace Bootstrap.DataAccess.MongoDB
// sort // sort
var sortBuilder = Builders<DataAccess.Trace>.Sort; var sortBuilder = Builders<DataAccess.Trace>.Sort;
SortDefinition<DataAccess.Trace> sort = null; var sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.LogTime) : sortBuilder.Descending(t => t.LogTime);
switch (po.Sort) switch (po.Sort)
{ {
case "LogTime":
sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.LogTime) : sortBuilder.Descending(t => t.LogTime);
break;
case "IP": case "IP":
sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.Ip) : sortBuilder.Descending(t => t.Ip); sort = po.Order == "asc" ? sortBuilder.Ascending(t => t.Ip) : sortBuilder.Descending(t => t.Ip);
break; break;
@ -73,7 +70,7 @@ namespace Bootstrap.DataAccess.MongoDB
/// <param name="endTime"></param> /// <param name="endTime"></param>
/// <param name="ip"></param> /// <param name="ip"></param>
/// <returns></returns> /// <returns></returns>
public override IEnumerable<DataAccess.Trace> RetrieveAll(DateTime? startTime, DateTime? endTime, string ip) public override IEnumerable<DataAccess.Trace> RetrieveAll(DateTime? startTime, DateTime? endTime, string? ip)
{ {
var filterBuilder = Builders<DataAccess.Trace>.Filter; var filterBuilder = Builders<DataAccess.Trace>.Filter;
var filter = filterBuilder.Empty; var filter = filterBuilder.Empty;

View File

@ -1,4 +1,5 @@
using Bootstrap.Security; using Bootstrap.Security;
using Bootstrap.Security.Mvc;
using Longbow.Security.Cryptography; using Longbow.Security.Cryptography;
using MongoDB.Driver; using MongoDB.Driver;
using System; using System;
@ -15,19 +16,19 @@ namespace Bootstrap.DataAccess.MongoDB
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public IEnumerable<string> Roles { get; set; } public IEnumerable<string> Roles { get; set; } = new List<string>();
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public IEnumerable<string> Groups { get; set; } public IEnumerable<string> Groups { get; set; } = new List<string>();
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="userName"></param> /// <param name="userName"></param>
/// <returns></returns> /// <returns></returns>
public override BootstrapUser RetrieveUserByUserName(string userName) public override BootstrapUser? RetrieveUserByUserName(string userName)
{ {
var project = Builders<User>.Projection.Include(u => u.Id) var project = Builders<User>.Projection.Include(u => u.Id)
.Include(u => u.UserName) .Include(u => u.UserName)
@ -39,7 +40,7 @@ namespace Bootstrap.DataAccess.MongoDB
if (ret != null) if (ret != null)
{ {
if (string.IsNullOrEmpty(ret.Icon)) ret.Icon = "default.jpg"; if (string.IsNullOrEmpty(ret.Icon)) ret.Icon = "default.jpg";
if (string.IsNullOrEmpty(ret.App)) ret.App = "0"; if (string.IsNullOrEmpty(ret.App)) ret.App = BootstrapAppContext.AppId;
} }
return ret; return ret;
} }
@ -118,8 +119,6 @@ namespace Bootstrap.DataAccess.MongoDB
RegisterTime = DateTime.Now, RegisterTime = DateTime.Now,
ApprovedTime = user.ApprovedTime, ApprovedTime = user.ApprovedTime,
ApprovedBy = user.ApprovedBy, ApprovedBy = user.ApprovedBy,
Roles = new List<string>(),
Groups = new List<string>(),
Icon = user.Icon, Icon = user.Icon,
Description = user.Description, Description = user.Description,
IsReset = 0 IsReset = 0

View File

@ -14,17 +14,17 @@ namespace Bootstrap.DataAccess
/// <summary> /// <summary>
/// 获得/设置 应用程序主键ID /// 获得/设置 应用程序主键ID
/// </summary> /// </summary>
public string Id { get; set; } public string? Id { get; set; }
/// <summary> /// <summary>
/// 获得/设置 群组名称 /// 获得/设置 群组名称
/// </summary> /// </summary>
public string AppName { get; set; } public string AppName { get; set; } = "未设置";
/// <summary> /// <summary>
/// 获取/设置 用户群组关联状态 checked 标示已经关联 '' 标示未关联 /// 获取/设置 用户群组关联状态 checked 标示已经关联 '' 标示未关联
/// </summary> /// </summary>
public string Checked { get; set; } public string Checked { get; set; } = "";
/// <summary> /// <summary>
/// 根据角色ID指派部门 /// 根据角色ID指派部门
@ -33,7 +33,8 @@ namespace Bootstrap.DataAccess
/// <returns></returns> /// <returns></returns>
public virtual IEnumerable<App> RetrievesByRoleId(string roleId) public virtual IEnumerable<App> RetrievesByRoleId(string roleId)
{ {
var ret = DbManager.Create().Fetch<App>($"select d.Code as Id, d.Name as AppName, case ra.AppId when d.Code then 'checked' else '' end Checked from Dicts d left join RoleApp ra on d.Code = ra.AppId and ra.RoleId = @1 where d.Code > '0' and d.Category = @0", "应用程序", roleId); using var db = DbManager.Create();
var ret = db.Fetch<App>($"select d.Code as Id, d.Name as AppName, case ra.AppId when d.Code then 'checked' else '' end Checked from Dicts d left join RoleApp ra on d.Code = ra.AppId and ra.RoleId = @1 where d.Category = @0", "应用程序", roleId);
// 判断是否为Administrators // 判断是否为Administrators
var role = RoleHelper.Retrieves().FirstOrDefault(r => r.Id == roleId); var role = RoleHelper.Retrieves().FirstOrDefault(r => r.Id == roleId);

View File

@ -11,36 +11,30 @@ namespace Bootstrap.DataAccess
/// </summary> /// </summary>
public class AutoDB public class AutoDB
{ {
private string _folder;
/// <summary> /// <summary>
/// 数据库检查方法 /// 数据库检查方法
/// </summary> /// </summary>
public virtual void CheckDB(string folder) public virtual void EnsureCreated(string folder)
{
_folder = folder;
using (var db = Longbow.Data.DbManager.Create())
{ {
using var db = Longbow.Data.DbManager.Create();
db.CommandTimeout = 5000; db.CommandTimeout = 5000;
switch (db.Provider.GetType().Name) switch (db.Provider.GetType().Name)
{ {
case "SQLiteDatabaseProvider": case "SQLiteDatabaseProvider":
if (db.ExecuteScalar<int>("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='Users'") == 0) GenerateSQLiteDB(db); if (db.ExecuteScalar<int>("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='Users'") == 0) GenerateSQLiteDB(db, folder);
break; break;
case "SqlServerDatabaseProvider": case "SqlServerDatabaseProvider":
using (var newDB = ModifyConnectionString(db)) using (var newDB = ModifyConnectionString(db))
{ {
if (newDB.ExecuteScalar<int?>("select COUNT(1) from sys.databases where name = N'BootstrapAdmin'") == 0) GenerateSqlServer(); if (newDB.ExecuteScalar<int?>("select COUNT(*) from sys.databases where name = N'BootstrapAdmin'") == 0) GenerateDB(folder);
} }
break; break;
case "MySqlDatabaseProvider": case "MySqlDatabaseProvider":
case "MariaDbDatabaseProvider": case "MariaDbDatabaseProvider":
// UNDONE: 本地没有环境此处代码未测试 if (db.ExecuteScalar<int>("select count(*) from information_schema.tables where table_name ='Users' and Table_Schema = 'BootstrapAdmin'") == 0) GenerateDB(folder);
if (db.ExecuteScalar<int>("select count(*) from information_schema.tables where table_name ='Users' and Table_Schema = 'BootstrapAdmin'") == 0) GenerateMySql();
break; break;
} }
} }
}
private IDatabase ModifyConnectionString(IDatabase db) private IDatabase ModifyConnectionString(IDatabase db)
{ {
@ -56,9 +50,8 @@ namespace Bootstrap.DataAccess
return new Database(string.Join(";", newsegs), provider); return new Database(string.Join(";", newsegs), provider);
} }
private void GenerateSQLiteDB(IDatabase db) private void GenerateSQLiteDB(IDatabase db, string folder)
{ {
var folder = _folder;
var initFile = Path.Combine(folder, "Install.sql"); var initFile = Path.Combine(folder, "Install.sql");
if (File.Exists(initFile)) if (File.Exists(initFile))
{ {
@ -74,21 +67,19 @@ namespace Bootstrap.DataAccess
} }
} }
private void GenerateSqlServer() /// <summary>
/// 执行建库脚本
/// </summary>
protected void GenerateDB(string folder)
{ {
// 检查 install.ps1 脚本 // 检查 install.ps1 脚本
var file = Path.Combine(_folder, $"install.ps1"); var file = Path.Combine(folder, $"install.ps1");
if (File.Exists(file)) if (File.Exists(file))
{ {
var psi = new ProcessStartInfo("powershell", $"{file} \"{_folder}\""); var psi = new ProcessStartInfo("powershell", $"{file} \"{folder}\"");
var p = Process.Start(psi); var p = Process.Start(psi);
p.WaitForExit(); p.WaitForExit();
} }
} }
private void GenerateMySql()
{
// UNDONE: 没有环境暂时未写代码
}
} }
} }

View File

@ -5,22 +5,22 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bootstrap.Security.DataAccess" Version="3.0.1-beta3" /> <PackageReference Include="Bootstrap.Security.DataAccess" Version="3.0.1" />
<PackageReference Include="Bootstrap.Security.Mvc" Version="3.0.2-beta7" /> <PackageReference Include="Bootstrap.Security.Mvc" Version="3.0.2" />
<PackageReference Include="Longbow" Version="3.0.0" /> <PackageReference Include="Longbow" Version="3.0.1" />
<PackageReference Include="Longbow.Cache" Version="3.0.1-beta2" /> <PackageReference Include="Longbow.Cache" Version="3.0.3-beta1" />
<PackageReference Include="Longbow.Data" Version="3.0.1-beta1" /> <PackageReference Include="Longbow.Data" Version="3.0.1" />
<PackageReference Include="Longbow.GiteeAuth" Version="3.0.1-beta1" /> <PackageReference Include="Longbow.GiteeAuth" Version="3.0.1" />
<PackageReference Include="Longbow.GitHubAuth" Version="3.0.1-beta1" /> <PackageReference Include="Longbow.GitHubAuth" Version="3.0.1" />
<PackageReference Include="Longbow.OAuth" Version="3.0.1" /> <PackageReference Include="Longbow.OAuth" Version="3.0.2" />
<PackageReference Include="Longbow.PetaPoco" Version="1.0.2" /> <PackageReference Include="Longbow.PetaPoco" Version="1.0.2" />
<PackageReference Include="Longbow.Security.Cryptography" Version="1.3.0" /> <PackageReference Include="Longbow.Security.Cryptography" Version="1.3.0" />
<PackageReference Include="Longbow.Tasks" Version="3.0.0" /> <PackageReference Include="Longbow.Tasks" Version="3.0.2-beta2" />
<PackageReference Include="Longbow.Web" Version="3.0.1-beta1" /> <PackageReference Include="Longbow.Web" Version="3.0.1" />
<PackageReference Include="Longbow.WeChatAuth" Version="3.0.0" /> <PackageReference Include="Longbow.WeChatAuth" Version="3.0.1" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="3.0.0" /> <PackageReference Include="Microsoft.Data.Sqlite" Version="3.0.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.7.0" /> <PackageReference Include="System.Data.SqlClient" Version="4.7.0" />
<PackageReference Include="PetaPoco.Extensions" Version="3.0.0" /> <PackageReference Include="PetaPoco.Extensions" Version="3.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -20,7 +20,7 @@ namespace Bootstrap.DataAccess
/// <param name="appIds"></param> /// <param name="appIds"></param>
/// <param name="dictIds"></param> /// <param name="dictIds"></param>
/// <param name="cacheKey"></param> /// <param name="cacheKey"></param>
public static void ClearCache(IEnumerable<string> roleIds = null, IEnumerable<string> userIds = null, IEnumerable<string> groupIds = null, IEnumerable<string> menuIds = null, IEnumerable<string> appIds = null, IEnumerable<string> dictIds = null, string cacheKey = null) public static void ClearCache(IEnumerable<string>? roleIds = null, IEnumerable<string>? userIds = null, IEnumerable<string>? groupIds = null, IEnumerable<string>? menuIds = null, IEnumerable<string>? appIds = null, IEnumerable<string>? dictIds = null, string? cacheKey = null)
{ {
var cacheKeys = new List<string>(); var cacheKeys = new List<string>();
var corsKeys = new List<string>(); var corsKeys = new List<string>();

View File

@ -10,20 +10,21 @@ namespace Bootstrap.DataAccess
[TableName("DBLogs")] [TableName("DBLogs")]
public class DBLog public class DBLog
{ {
/// <summary> /// <summary>
/// 获得/设置 主键ID /// 获得/设置 主键ID
/// </summary> /// </summary>
public string Id { get; set; } public string? Id { get; set; }
/// <summary> /// <summary>
/// 获得/设置 当前登陆名 /// 获得/设置 当前登陆名
/// </summary> /// </summary>
public string UserName { get; set; } public string? UserName { get; set; }
/// <summary> /// <summary>
/// 获得/设置 数据库执行脚本 /// 获得/设置 数据库执行脚本
/// </summary> /// </summary>
public string SQL { get; set; } public string SQL { get; set; } = "";
/// <summary> /// <summary>
/// 获取/设置 用户角色关联状态 checked 标示已经关联 '' 标示未关联 /// 获取/设置 用户角色关联状态 checked 标示已经关联 '' 标示未关联
@ -38,8 +39,10 @@ namespace Bootstrap.DataAccess
/// <param name="endTime"></param> /// <param name="endTime"></param>
/// <param name="userName"></param> /// <param name="userName"></param>
/// <returns></returns> /// <returns></returns>
public virtual Page<DBLog> RetrievePages(PaginationOption po, DateTime? startTime, DateTime? endTime, string userName) public virtual Page<DBLog> RetrievePages(PaginationOption po, DateTime? startTime, DateTime? endTime, string? userName)
{ {
if (string.IsNullOrEmpty(po.Sort)) po.Sort = "LogTime";
if (string.IsNullOrEmpty(po.Order)) po.Order = "desc";
var sql = new Sql("select * from DBLogs"); var sql = new Sql("select * from DBLogs");
if (startTime.HasValue) sql.Where("LogTime >= @0", startTime.Value); if (startTime.HasValue) sql.Where("LogTime >= @0", startTime.Value);
if (endTime.HasValue) sql.Where("LogTime < @0", endTime.Value.AddDays(1).AddSeconds(-1)); if (endTime.HasValue) sql.Where("LogTime < @0", endTime.Value.AddDays(1).AddSeconds(-1));
@ -47,7 +50,8 @@ namespace Bootstrap.DataAccess
if (!string.IsNullOrEmpty(userName)) sql.Where("UserName = @0", userName); if (!string.IsNullOrEmpty(userName)) sql.Where("UserName = @0", userName);
sql.OrderBy($"{po.Sort} {po.Order}"); sql.OrderBy($"{po.Sort} {po.Order}");
return DbManager.Create().Page<DBLog>(po.PageIndex, po.Limit, sql); using var db = DbManager.Create();
return db.Page<DBLog>(po.PageIndex, po.Limit, sql);
} }
/// <summary> /// <summary>
@ -72,7 +76,8 @@ namespace Bootstrap.DataAccess
{ {
if (p == null) throw new ArgumentNullException(nameof(p)); if (p == null) throw new ArgumentNullException(nameof(p));
DeleteLogAsync(); DeleteLogAsync();
DbManager.Create(enableLog: false).Save(p); using var db = DbManager.Create(enableLog: false);
db.Save(p);
return true; return true;
} }
} }

View File

@ -10,18 +10,19 @@ namespace Bootstrap.DataAccess
/// <summary> /// <summary>
/// 创建数据库实体类时发生异常实例 /// 创建数据库实体类时发生异常实例
/// </summary> /// </summary>
public static Exception Exception { get; set; } public static Exception? Exception { get; private set; }
/// <summary> /// <summary>
/// 根据配置文件动态创建数据库实体类方法 /// 根据配置文件动态创建数据库实体类方法
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static T Create<T>() public static T? Create<T>() where T : class
{ {
T t = default; T? t = default;
try try
{ {
Exception = null;
t = Longbow.Data.DbContextManager.Create<T>(); t = Longbow.Data.DbContextManager.Create<T>();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -20,7 +20,7 @@ namespace Bootstrap.DataAccess
/// <param name="keepAlive"></param> /// <param name="keepAlive"></param>
/// <param name="enableLog">是否记录日志</param> /// <param name="enableLog">是否记录日志</param>
/// <returns></returns> /// <returns></returns>
public static IDatabase Create(string connectionName = null, bool keepAlive = false, bool enableLog = true) public static IDatabase Create(string? connectionName = null, bool keepAlive = false, bool enableLog = true)
{ {
if (Mappers.GetMapper(typeof(Exceptions), null) == null) Mappers.Register(typeof(Exceptions).Assembly, new BootstrapDataAccessConventionMapper()); if (Mappers.GetMapper(typeof(Exceptions), null) == null) Mappers.Register(typeof(Exceptions).Assembly, new BootstrapDataAccessConventionMapper());
var db = Longbow.Data.DbManager.Create(connectionName, keepAlive); var db = Longbow.Data.DbManager.Create(connectionName, keepAlive);

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