Browse Source

Merge pull request #65 from colinin/3.0

backlog of modifications
pull/81/head
cKey 5 years ago
committed by GitHub
parent
commit
14af167749
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      aspnet-core/configuration/messages/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json
  2. 13
      aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs
  3. 56
      aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPExecutionFailedException.cs
  4. 9
      aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs
  5. 40
      aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantCreateEventHandler.cs
  6. 40
      aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantUpdateEventHandler.cs
  7. 7
      aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs
  8. 4
      aspnet-core/services/account/AuthServer.Host/AuthServer.Host.csproj
  9. 2
      aspnet-core/services/account/AuthServer.Host/Dockerfile
  10. 38
      aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs
  11. 26
      aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs
  12. 77
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs
  13. 93
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Authorization/HangfireDashboardAuthorizationFilter.cs
  14. 74
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/DashboardOptionsExtensions.cs
  15. 47
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireApplicationBuilderExtensions.cs
  16. 55
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireDashboardRouteOptions.cs
  17. 31
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireJwtTokenMiddleware.cs
  18. 8
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj
  19. 4
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/en.json
  20. 4
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/zh-Hans.json
  21. 0
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Notifications/MessageServiceDefinitionProvider.cs
  22. 22
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissionDefinitionProvider.cs
  23. 12
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissions.cs
  24. 0
      aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Utils/SnowflakeIdGenerator.cs
  25. 33
      aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/AppPlatformHttpApiHostModule.cs
  26. 49
      vueJs/src/components/HangfireDashboard/index.vue
  27. 2
      vueJs/vue.config.js

3
aspnet-core/configuration/messages/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json

@ -1,4 +1,7 @@
{
"App": {
"CorsOrigins": "http://localhost:9527,http://127.0.0.1:30000"
},
"ConnectionStrings": {
"Default": "Server=127.0.0.1;Database=Messages;User Id=root;Password=123456",
"MessageService": "Server=127.0.0.1;Database=Messages;User Id=root;Password=123456",

13
aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs

@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.EventBus;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.EventBus.CAP
@ -27,6 +28,18 @@ namespace LINGYUN.Abp.EventBus.CAP
{
configuration.GetSection("CAP:EventBus").Bind(options);
context.Services.ExecutePreConfiguredActions(options);
//if (options.FailedThresholdCallback == null)
//{
// options.FailedThresholdCallback = async (failed) =>
// {
// var exceptionNotifier = failed.ServiceProvider.GetService<IExceptionNotifier>();
// if (exceptionNotifier != null)
// {
// // TODO: 作为异常处理?
// await exceptionNotifier.NotifyAsync(new AbpCAPExecutionFailedException(failed.MessageType, failed.Message));
// }
// };
//}
});
}

56
aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPExecutionFailedException.cs

@ -0,0 +1,56 @@
using DotNetCore.CAP.Messages;
using System;
using Volo.Abp;
namespace LINGYUN.Abp.EventBus.CAP
{
/// <summary>
/// AbpECAPExecutionFailedException
/// </summary>
public class AbpCAPExecutionFailedException : AbpException
{
/// <summary>
/// MessageType
/// </summary>
public MessageType MessageType { get; set; }
/// <summary>
/// Message
/// </summary>
public Message Origin { get; set; }
/// <summary>
/// constructor
/// </summary>
/// <param name="messageType"></param>
/// <param name="prigin"></param>
public AbpCAPExecutionFailedException(MessageType messageType, Message prigin)
{
MessageType = messageType;
Origin = prigin;
}
/// <summary>
/// constructor
/// </summary>
/// <param name="messageType"></param>
/// <param name="prigin"></param>
/// <param name="message"></param>
public AbpCAPExecutionFailedException(MessageType messageType, Message prigin, string message) : base(message)
{
MessageType = messageType;
Origin = prigin;
}
/// <summary>
/// constructor
/// </summary>
/// <param name="messageType"></param>
/// <param name="prigin"></param>
/// <param name="message"></param>
/// <param name="innerException"></param>
public AbpCAPExecutionFailedException(MessageType messageType, Message prigin, string message, Exception innerException) : base(message, innerException)
{
MessageType = messageType;
Origin = prigin;
}
}
}

9
aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs

@ -92,10 +92,11 @@ namespace LINGYUN.Abp.SettingManagement
continue;
}
if (!setting.IsVisibleToClients)
{
continue;
}
// 既然是配置服务,那必须能管理所有配置才对
//if (!setting.IsVisibleToClients)
//{
// continue;
//}
var settingValue = await SettingManager.GetOrNullAsync(setting.Name, providerName, providerKey);
var settingInfo = new SettingDto

40
aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantCreateEventHandler.cs

@ -1,4 +1,6 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
@ -12,6 +14,7 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed
{
public class TenantCreateEventHandler : IDistributedEventHandler<EntityCreatedEto<TenantEto>>, ITransientDependency
{
private readonly ILogger<TenantCreateEventHandler> _logger;
private readonly ICurrentTenant _currentTenant;
private readonly ITenantRepository _tenantRepository;
private readonly IDistributedCache<TenantConfigurationCacheItem> _cache;
@ -19,9 +22,11 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed
public TenantCreateEventHandler(
ICurrentTenant currentTenant,
ITenantRepository tenantRepository,
ILogger<TenantCreateEventHandler> logger,
IDistributedCache<TenantConfigurationCacheItem> cache)
{
_cache = cache;
_logger = logger;
_currentTenant = currentTenant;
_tenantRepository = tenantRepository;
}
@ -29,22 +34,29 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityCreatedEto<TenantEto> eventData)
{
using (_currentTenant.Change(null))
try
{
var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true);
if (tenant == null)
using (_currentTenant.Change(null))
{
return;
}
var connectionStrings = new ConnectionStrings();
foreach (var tenantConnectionString in tenant.ConnectionStrings)
{
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value;
}
var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings);
var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true);
if (tenant == null)
{
return;
}
var connectionStrings = new ConnectionStrings();
foreach (var tenantConnectionString in tenant.ConnectionStrings)
{
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value;
}
var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString());
await _cache.SetAsync(cacheKey, cacheItem);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString());
await _cache.SetAsync(cacheKey, cacheItem);
}
}
catch (Exception ex)
{
_logger.LogException(ex);
}
}
}

40
aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantUpdateEventHandler.cs

@ -1,4 +1,6 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
@ -12,6 +14,7 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed
{
public class TenantUpdateEventHandler : IDistributedEventHandler<EntityUpdatedEto<TenantEto>>, ITransientDependency
{
private readonly ILogger<TenantUpdateEventHandler> _logger;
private readonly ICurrentTenant _currentTenant;
private readonly ITenantRepository _tenantRepository;
private readonly IDistributedCache<TenantConfigurationCacheItem> _cache;
@ -19,9 +22,11 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed
public TenantUpdateEventHandler(
ICurrentTenant currentTenant,
ITenantRepository tenantRepository,
ILogger<TenantUpdateEventHandler> logger,
IDistributedCache<TenantConfigurationCacheItem> cache)
{
_cache = cache;
_logger = logger;
_currentTenant = currentTenant;
_tenantRepository = tenantRepository;
}
@ -29,22 +34,29 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityUpdatedEto<TenantEto> eventData)
{
using (_currentTenant.Change(null))
try
{
var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true);
if (tenant == null)
using (_currentTenant.Change(null))
{
return;
}
var connectionStrings = new ConnectionStrings();
foreach (var tenantConnectionString in tenant.ConnectionStrings)
{
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value;
}
var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings);
var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true);
if (tenant == null)
{
return;
}
var connectionStrings = new ConnectionStrings();
foreach (var tenantConnectionString in tenant.ConnectionStrings)
{
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value;
}
var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString());
await _cache.SetAsync(cacheKey, cacheItem);
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString());
await _cache.SetAsync(cacheKey, cacheItem);
}
}
catch(Exception ex)
{
_logger.LogException(ex);
}
}
}

7
aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs

@ -144,7 +144,7 @@ namespace AuthServer.Host
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]);
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "AuthServer-Protection-Keys");
@ -183,7 +183,10 @@ namespace AuthServer.Host
app.UseIdentityServer();
app.UseAuditing();
SeedData(context);
if (context.GetEnvironment().IsDevelopment())
{
SeedData(context);
}
}
private void SeedData(ApplicationInitializationContext context)

4
aspnet-core/services/account/AuthServer.Host/AuthServer.Host.csproj

@ -40,7 +40,5 @@
<ProjectReference Include="..\..\..\modules\tenants\LINGYUN.Abp.MultiTenancy.DbFinder\LINGYUN.Abp.MultiTenancy.DbFinder.csproj" />
<ProjectReference Include="..\..\..\modules\tenants\LINGYUN.Abp.MultiTenancy\LINGYUN.Abp.MultiTenancy.csproj" />
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties appsettings_1development_1json__JsonSchema="http://json.schemastore.org/avro-avsc" /></VisualStudio></ProjectExtensions>
</Project>

2
aspnet-core/services/account/AuthServer.Host/Dockerfile

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS runtime
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS runtime
LABEL maintainer="colin.in@foxmail.com"
WORKDIR /app

38
aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs

@ -25,7 +25,6 @@ using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp;
using Volo.Abp.Account;
@ -148,7 +147,14 @@ namespace LINGYUN.BackendAdmin
Configure<AbpExceptionHandlingOptions>(options =>
{
// 加入需要处理的异常类型
options.Handlers.Add<AbpException>();
options.Handlers.Add<Volo.Abp.Data.AbpDbConcurrencyException>();
options.Handlers.Add<AbpInitializationException>();
options.Handlers.Add<ObjectDisposedException>();
options.Handlers.Add<StackOverflowException>();
options.Handlers.Add<OutOfMemoryException>();
options.Handlers.Add<System.Data.Common.DbException>();
options.Handlers.Add<Microsoft.EntityFrameworkCore.DbUpdateException>();
options.Handlers.Add<System.Data.DBConcurrencyException>();
});
// 自定义需要发送邮件通知的异常类型
Configure<AbpEmailExceptionHandlingOptions>(options =>
@ -157,8 +163,6 @@ namespace LINGYUN.BackendAdmin
options.SendStackTrace = true;
// 未指定异常接收者的默认接收邮件
options.DefaultReceiveEmail = "colin.in@foxmail.com";
// 指定某种异常发送到哪个邮件
options.HandReceivedException<AbpException>("colin.in@foxmail.com");
});
@ -208,6 +212,25 @@ namespace LINGYUN.BackendAdmin
options.SwaggerDoc("v1", new OpenApiInfo { Title = "BackendAdmin API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "bearer",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] { }
}
});
});
// 支持本地化语言类型
@ -245,7 +268,7 @@ namespace LINGYUN.BackendAdmin
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]);
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "BackendAdmin-Protection-Keys");
@ -283,7 +306,10 @@ namespace LINGYUN.BackendAdmin
// 路由
app.UseConfiguredEndpoints();
SeedData(context);
if (context.GetEnvironment().IsDevelopment())
{
SeedData(context);
}
}
private void SeedData(ApplicationInitializationContext context)

26
aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs

@ -128,6 +128,25 @@ namespace LINGYUN.ApiGateway
options.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiGateway API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "bearer",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] { }
}
});
});
// 支持本地化语言类型
@ -152,7 +171,7 @@ namespace LINGYUN.ApiGateway
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]);
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "ApiGateway-Protection-Keys");
@ -191,7 +210,10 @@ namespace LINGYUN.ApiGateway
// 路由
app.UseConfiguredEndpoints();
SeedData(context);
if (context.GetEnvironment().IsDevelopment())
{
SeedData(context);
}
}
private void SeedData(ApplicationInitializationContext context)

77
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs → aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs

@ -8,6 +8,7 @@ using LINGYUN.Abp.ExceptionHandling;
using LINGYUN.Abp.ExceptionHandling.Notifications;
using LINGYUN.Abp.Hangfire.Storage.MySql;
using LINGYUN.Abp.IM.SignalR;
using LINGYUN.Abp.MessageService.Authorization;
using LINGYUN.Abp.MessageService.EntityFrameworkCore;
using LINGYUN.Abp.MessageService.Localization;
using LINGYUN.Abp.MessageService.MultiTenancy;
@ -15,15 +16,18 @@ using LINGYUN.Abp.MultiTenancy.DbFinder;
using LINGYUN.Abp.Notifications.SignalR;
using LINGYUN.Abp.Notifications.WeChat.WeApp;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using StackExchange.Redis;
using System;
using System.Linq;
using System.Text;
using Volo.Abp;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
@ -67,6 +71,8 @@ namespace LINGYUN.Abp.MessageService
)]
public class AbpMessageServiceHttpApiHostModule : AbpModule
{
private const string DefaultCorsPolicyName = "Default";
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
@ -104,7 +110,14 @@ namespace LINGYUN.Abp.MessageService
Configure<AbpExceptionHandlingOptions>(options =>
{
// 加入需要处理的异常类型
// options.Handlers.Add<Volo.Abp.Authorization.AbpAuthorizationException>();
options.Handlers.Add<Volo.Abp.Data.AbpDbConcurrencyException>();
options.Handlers.Add<AbpInitializationException>();
options.Handlers.Add<ObjectDisposedException>();
options.Handlers.Add<StackOverflowException>();
options.Handlers.Add<OutOfMemoryException>();
options.Handlers.Add<System.Data.Common.DbException>();
options.Handlers.Add<Microsoft.EntityFrameworkCore.DbUpdateException>();
options.Handlers.Add<System.Data.DBConcurrencyException>();
});
Configure<AbpDistributedCacheOptions>(options =>
@ -161,7 +174,55 @@ namespace LINGYUN.Abp.MessageService
options.SwaggerDoc("v1", new OpenApiInfo { Title = "MessageService API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "bearer",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] { }
}
});
});
context.Services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
Configure<HangfireDashboardRouteOptions>(options =>
{
options.WithOrigins(
configuration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
);
});
// 支持本地化语言类型
Configure<AbpLocalizationOptions>(options =>
@ -171,7 +232,7 @@ namespace LINGYUN.Abp.MessageService
options.Resources
.Get<MessageServiceResource>()
.AddVirtualJson("/LINGYUN/Abp/MessageService/Localization/HttpApiHost");
.AddVirtualJson("/Localization/HttpApiHost");
});
context.Services.AddAuthentication("Bearer")
@ -188,7 +249,7 @@ namespace LINGYUN.Abp.MessageService
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]);
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "MessageService-Protection-Keys");
@ -214,8 +275,12 @@ namespace LINGYUN.Abp.MessageService
app.UseAbpRequestLocalization();
//路由
app.UseRouting();
// 跨域
app.UseCors(DefaultCorsPolicyName);
// 加入自定义中间件
app.UseSignalRJwtToken();
// TODO: 还有没有其他方法在iframe中传递身份令牌?
app.UseHangfireJwtToken();
// 认证
app.UseAuthentication();
// jwt
@ -234,7 +299,11 @@ namespace LINGYUN.Abp.MessageService
// 审计日志
app.UseAuditing();
app.UseHangfireServer();
app.UseHangfireDashboard();
app.UseHangfireDashboard(options =>
{
options.IgnoreAntiforgeryToken = true;
options.UseAuthorization(new HangfireDashboardAuthorizationFilter());
});
// 路由
app.UseConfiguredEndpoints();
}

93
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Authorization/HangfireDashboardAuthorizationFilter.cs

@ -0,0 +1,93 @@
using Hangfire;
using Hangfire.Annotations;
using Hangfire.Dashboard;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NUglify.Helpers;
using System.Linq;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.MessageService.Authorization
{
public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter
{
protected string[] AllowGrantPath { get; }
public HangfireDashboardAuthorizationFilter()
{
AllowGrantPath = new string[] { "/css", "/js", "/fonts", "/stats" };
}
public bool Authorize([NotNull] DashboardContext context)
{
// 本地请求
if (LocalRequestOnlyAuthorize(context))
{
return true;
}
// 放行路径
if (AllowGrantPath.Contains(context.Request.Path))
{
return true;
}
var httpContext = context.GetHttpContext();
var options = httpContext.RequestServices.GetService<IOptions<HangfireDashboardRouteOptions>>()?.Value;
if (options != null)
{
// 请求路径对应的权限检查
// TODO: 怎么来传递用户身份令牌?
var permission = options.GetPermission(context.Request.Path);
if (!permission.IsNullOrWhiteSpace())
{
var permissionChecker = httpContext.RequestServices.GetRequiredService<IPermissionChecker>();
return AsyncHelper.RunSync(async () => await permissionChecker.IsGrantedAsync(permission));
}
}
return false;
}
public override int GetHashCode()
{
// 类型相同就行了
return GetType().FullName.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
// 类型相同就行了
if (GetType().Equals(obj.GetType()))
{
return true;
}
return base.Equals(obj);
}
protected virtual bool LocalRequestOnlyAuthorize(DashboardContext context)
{
if (string.IsNullOrEmpty(context.Request.RemoteIpAddress))
{
return false;
}
if (context.Request.RemoteIpAddress == "127.0.0.1" || context.Request.RemoteIpAddress == "::1")
{
return true;
}
if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress)
{
return true;
}
return false;
}
}
}

74
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/DashboardOptionsExtensions.cs

@ -0,0 +1,74 @@
using Hangfire.Dashboard;
using JetBrains.Annotations;
using System.Collections.Generic;
using Volo.Abp;
namespace Hangfire
{
public static class DashboardOptionsExtensions
{
public static DashboardOptions AddAuthorization(
[NotNull] this DashboardOptions options,
[NotNull] IDashboardAuthorizationFilter authorizationFilter)
{
Check.NotNull(options, nameof(options));
Check.NotNull(authorizationFilter, nameof(authorizationFilter));
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>();
filters.AddRange(options.Authorization);
filters.AddIfNotContains(authorizationFilter);
options.Authorization = filters;
return options;
}
public static DashboardOptions AddAuthorizations(
[NotNull] this DashboardOptions options,
[NotNull] IEnumerable<IDashboardAuthorizationFilter> authorizationFilters)
{
Check.NotNull(options, nameof(options));
Check.NotNull(authorizationFilters, nameof(authorizationFilters));
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>();
filters.AddRange(options.Authorization);
filters.AddIfNotContains(authorizationFilters);
options.Authorization = filters;
return options;
}
public static DashboardOptions UseAuthorization(
[NotNull] this DashboardOptions options,
[NotNull] IDashboardAuthorizationFilter authorizationFilter)
{
Check.NotNull(options, nameof(options));
Check.NotNull(authorizationFilter, nameof(authorizationFilter));
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>
{
authorizationFilter
};
options.Authorization = filters;
return options;
}
public static DashboardOptions UseAuthorizations(
[NotNull] this DashboardOptions options,
[NotNull] IEnumerable<IDashboardAuthorizationFilter> authorizationFilters)
{
Check.NotNull(options, nameof(options));
Check.NotNull(authorizationFilters, nameof(authorizationFilters));
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>();
filters.AddRange(authorizationFilters);
options.Authorization = filters;
return options;
}
}
}

47
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireApplicationBuilderExtensions.cs

@ -0,0 +1,47 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using System;
using Volo.Abp;
namespace Hangfire
{
public static class HangfireApplicationBuilderExtensions
{
public static IApplicationBuilder UseHangfireJwtToken(
[NotNull] this IApplicationBuilder app)
{
return app.UseMiddleware<HangfireJwtTokenMiddleware>();
}
public static IApplicationBuilder UseHangfireDashboard(
[NotNull] this IApplicationBuilder app,
[CanBeNull] Action<DashboardOptions> setup = null)
{
Check.NotNull(app, nameof(app));
return app.UseHangfireDashboard("/hangfire", setup, null);
}
public static IApplicationBuilder UseHangfireDashboard(
[NotNull] this IApplicationBuilder app,
[CanBeNull] string pathMatch = "/hangfire",
[CanBeNull] Action<DashboardOptions> setup = null)
{
Check.NotNull(app, nameof(app));
return app.UseHangfireDashboard(pathMatch, setup, null);
}
public static IApplicationBuilder UseHangfireDashboard(
[NotNull] this IApplicationBuilder app,
[CanBeNull] string pathMatch = "/hangfire",
[CanBeNull] Action<DashboardOptions> setup = null,
[CanBeNull] JobStorage storage = null)
{
Check.NotNull(app, nameof(app));
var options = new DashboardOptions();
setup?.Invoke(options);
return app.UseHangfireDashboard(pathMatch: pathMatch, options: options, storage: storage);
}
}
}

55
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireDashboardRouteOptions.cs

@ -0,0 +1,55 @@
using LINGYUN.Abp.MessageService.Permissions;
using System.Collections.Generic;
using System.Linq;
namespace Hangfire
{
public class HangfireDashboardRouteOptions
{
public IList<string> AllowFrameOrigins { get; }
public IDictionary<string, string> RoutePermissions { get; }
public HangfireDashboardRouteOptions()
{
AllowFrameOrigins = new List<string>();
RoutePermissions = new Dictionary<string, string>();
InitDefaultRoutes();
}
public void WithOrigins(params string[] origins)
{
AllowFrameOrigins.AddIfNotContains(origins);
}
public void WithPermission(string route, string permission)
{
RoutePermissions.Add(route, permission);
}
public string GetPermission(string route)
{
var permission = RoutePermissions
.Where(x => x.Key.StartsWith(route))
.Select(x => x.Value)
.FirstOrDefault();
return permission;
}
private void InitDefaultRoutes()
{
WithPermission("/hangfire", AbpMessageServicePermissions.Hangfire.Default);
WithPermission("/stats", AbpMessageServicePermissions.Hangfire.Default);
WithPermission("/servers", AbpMessageServicePermissions.Hangfire.Default);
WithPermission("/retries", AbpMessageServicePermissions.Hangfire.Default);
WithPermission("/recurring", AbpMessageServicePermissions.Hangfire.Default);
WithPermission("/jobs/enqueued", AbpMessageServicePermissions.Hangfire.ManageQueue);
WithPermission("/jobs/processing", AbpMessageServicePermissions.Hangfire.ManageQueue);
WithPermission("/jobs/scheduled", AbpMessageServicePermissions.Hangfire.ManageQueue);
WithPermission("/jobs/failed", AbpMessageServicePermissions.Hangfire.ManageQueue);
WithPermission("/jobs/deleted", AbpMessageServicePermissions.Hangfire.ManageQueue);
WithPermission("/jobs/awaiting", AbpMessageServicePermissions.Hangfire.ManageQueue);
WithPermission("/jobs/actions", AbpMessageServicePermissions.Hangfire.ManageQueue);
WithPermission("/jobs/details", AbpMessageServicePermissions.Hangfire.ManageQueue);
}
}
}

31
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireJwtTokenMiddleware.cs

@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Hangfire
{
public class HangfireJwtTokenMiddleware : IMiddleware, ITransientDependency
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// 通过 iframe 加载页面的话,需要手动传递 access_token 到参数列表
if (context.Request.Path.StartsWithSegments("/hangfire") && context.User.Identity?.IsAuthenticated != true)
{
if (context.Request.Query.TryGetValue("access_token", out var accessTokens))
{
context.Request.Headers.Add("Authorization", accessTokens);
}
var options = context.RequestServices.GetService<IOptions<HangfireDashboardRouteOptions>>()?.Value;
if (options != null && options.AllowFrameOrigins.Count > 0)
{
// 跨域 iframe
context.Response.Headers.TryAdd("X-Frame-Options", $"\"ALLOW-FROM {options.AllowFrameOrigins.JoinAsString(",")}\"");
}
}
await next(context);
}
}
}

8
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj

@ -6,13 +6,13 @@
</PropertyGroup>
<ItemGroup>
<Content Remove="LINGYUN\Abp\MessageService\Localization\HttpApiHost\en.json" />
<Content Remove="LINGYUN\Abp\MessageService\Localization\HttpApiHost\zh-Hans.json" />
<Content Remove="Localization\HttpApiHost\en.json" />
<Content Remove="Localization\HttpApiHost\zh-Hans.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\MessageService\Localization\HttpApiHost\en.json" />
<EmbeddedResource Include="LINGYUN\Abp\MessageService\Localization\HttpApiHost\zh-Hans.json" />
<EmbeddedResource Include="Localization\HttpApiHost\en.json" />
<EmbeddedResource Include="Localization\HttpApiHost\zh-Hans.json" />
</ItemGroup>
<ItemGroup>

4
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/en.json → aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/en.json

@ -2,6 +2,8 @@
"culture": "en",
"texts": {
"NewTenantRegisteredNotificationTitle": "Tenants create notification",
"NewTenantRegisteredNotificationMessage": "New tenants to create success, the tenant name: {0}!"
"NewTenantRegisteredNotificationMessage": "New tenants to create success, the tenant name: {0}!",
"Permission:Hangfire": "Hangfire dashboard",
"Permission:ManageQueue": "Manage queue"
}
}

4
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/zh-Hans.json → aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/zh-Hans.json

@ -2,6 +2,8 @@
"culture": "zh-Hans",
"texts": {
"NewTenantRegisteredNotificationTitle": "租户创建通知",
"NewTenantRegisteredNotificationMessage": "新租户创建成功,租户名称:{0}!"
"NewTenantRegisteredNotificationMessage": "新租户创建成功,租户名称:{0}!",
"Permission:Hangfire": "Hangfire仪表板",
"Permission:ManageQueue": "管理队列"
}
}

0
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Notifications/MessageServiceDefinitionProvider.cs → aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Notifications/MessageServiceDefinitionProvider.cs

22
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissionDefinitionProvider.cs

@ -0,0 +1,22 @@
using LINGYUN.Abp.MessageService.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.MessageService.Permissions
{
public class AbpMessageServicePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var group = context.GetGroup(MessageServicePermissions.GroupName);
var hangfirePermission = group.AddPermission(AbpMessageServicePermissions.Hangfire.Default, L("Permission:Hangfire"));
hangfirePermission.AddChild(AbpMessageServicePermissions.Hangfire.ManageQueue, L("Permission:ManageQueue"));
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<MessageServiceResource>(name);
}
}
}

12
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissions.cs

@ -0,0 +1,12 @@
namespace LINGYUN.Abp.MessageService.Permissions
{
public class AbpMessageServicePermissions
{
public class Hangfire
{
public const string Default = MessageServicePermissions.GroupName + ".Hangfire";
public const string ManageQueue = Default + ".ManageQueue";
}
}
}

0
aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Utils/SnowflakeIdGenerator.cs → aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Utils/SnowflakeIdGenerator.cs

33
aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/AppPlatformHttpApiHostModule.cs

@ -1,4 +1,5 @@
using DotNetCore.CAP;
using DotNetCore.CAP.Internal;
using IdentityModel;
using LINGYUN.Abp.Domain.Entities.Events;
using LINGYUN.Abp.EventBus.CAP;
@ -121,7 +122,14 @@ namespace LINGYUN.Platform
Configure<AbpExceptionHandlingOptions>(options =>
{
// 加入需要处理的异常类型
options.Handlers.Add<AbpException>();
options.Handlers.Add<Volo.Abp.Data.AbpDbConcurrencyException>();
options.Handlers.Add<AbpInitializationException>();
options.Handlers.Add<ObjectDisposedException>();
options.Handlers.Add<StackOverflowException>();
options.Handlers.Add<OutOfMemoryException>();
options.Handlers.Add<System.Data.Common.DbException>();
options.Handlers.Add<Microsoft.EntityFrameworkCore.DbUpdateException>();
options.Handlers.Add<System.Data.DBConcurrencyException>();
});
// 自定义需要发送邮件通知的异常类型
Configure<AbpEmailExceptionHandlingOptions>(options =>
@ -130,8 +138,6 @@ namespace LINGYUN.Platform
options.SendStackTrace = true;
// 未指定异常接收者的默认接收邮件
options.DefaultReceiveEmail = "colin.in@foxmail.com";
// 指定某种异常发送到哪个邮件
options.HandReceivedException<AbpException>("colin.in@foxmail.com");
});
Configure<AbpDistributedCacheOptions>(options =>
@ -180,6 +186,25 @@ namespace LINGYUN.Platform
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Platform API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "bearer",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] { }
}
});
});
// 支持本地化语言类型
@ -214,7 +239,7 @@ namespace LINGYUN.Platform
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]);
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "Platform-Protection-Keys");

49
vueJs/src/components/HangfireDashboard/index.vue

@ -0,0 +1,49 @@
<template>
<div class="dashboard-container">
<iframe
id="iframe-hangifre"
ref="hangfireIframe"
:src="hangfireUrl"
scrolling="auto"
frameborder="0"
class="iframe-hangifre"
/>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component({
name: 'HangfireDashboard'
})
export default class extends Vue {
@Prop({ default: '' })
private hangfireUrl!: string
get showIframe() {
if (this.hangfireUrl) {
return true
}
return false
}
mounted() {
const hangfireFrame = this.$refs.hangfireIframe as any
if (hangfireFrame) {
const deviceWidth = document.documentElement.clientWidth
const deviceHeight = document.documentElement.clientHeight
hangfireFrame.style.width = deviceWidth + 'px'
hangfireFrame.style.height = deviceHeight + 'px'
}
}
}
</script>
<style lang="scss" scoped>
.iframe-hangifre {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>

2
vueJs/vue.config.js

@ -8,7 +8,7 @@ const devServerPort = 9527 // TODO: get this variable from setting.ts
const name = 'Vue Typescript Admin' // TODO: get this variable from setting.ts
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/vue-typescript-admin-template/' : '/',
publicPath: process.env.NODE_ENV === 'production' ? '/' : '/vue-typescript-admin-template/',
lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false,
devServer: {

Loading…
Cancel
Save