22 changed files with 858 additions and 613 deletions
@ -1,277 +0,0 @@ |
|||||
using Microsoft.Extensions.Configuration; |
|
||||
using OpenIddict.Abstractions; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Globalization; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.Authorization.Permissions; |
|
||||
using Volo.Abp.Data; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.MultiTenancy; |
|
||||
using Volo.Abp.OpenIddict.Applications; |
|
||||
using Volo.Abp.OpenIddict.Scopes; |
|
||||
using Volo.Abp.PermissionManagement; |
|
||||
|
|
||||
namespace LY.MicroService.Applications.Single.EntityFrameworkCore.DataSeeder; |
|
||||
|
|
||||
public class ClientDataSeederContributor : IDataSeedContributor, ITransientDependency |
|
||||
{ |
|
||||
private readonly IOpenIddictApplicationManager _applicationManager; |
|
||||
private readonly IOpenIddictApplicationRepository _applicationRepository; |
|
||||
|
|
||||
private readonly IOpenIddictScopeManager _scopeManager; |
|
||||
private readonly IOpenIddictScopeRepository _scopeRepository; |
|
||||
|
|
||||
private readonly IPermissionDataSeeder _permissionDataSeeder; |
|
||||
private readonly IConfiguration _configuration; |
|
||||
private readonly ICurrentTenant _currentTenant; |
|
||||
|
|
||||
public ClientDataSeederContributor( |
|
||||
IOpenIddictApplicationManager applicationManager, |
|
||||
IOpenIddictApplicationRepository applicationRepository, |
|
||||
IOpenIddictScopeManager scopeManager, |
|
||||
IOpenIddictScopeRepository scopeRepository, |
|
||||
IPermissionDataSeeder permissionDataSeeder, |
|
||||
IConfiguration configuration, |
|
||||
ICurrentTenant currentTenant) |
|
||||
{ |
|
||||
_applicationManager = applicationManager; |
|
||||
_applicationRepository = applicationRepository; |
|
||||
_scopeManager = scopeManager; |
|
||||
_scopeRepository = scopeRepository; |
|
||||
_permissionDataSeeder = permissionDataSeeder; |
|
||||
_configuration = configuration; |
|
||||
_currentTenant = currentTenant; |
|
||||
} |
|
||||
|
|
||||
public async virtual Task SeedAsync(DataSeedContext context) |
|
||||
{ |
|
||||
using (_currentTenant.Change(context.TenantId)) |
|
||||
{ |
|
||||
await SeedOpenIddictAsync(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#region OpenIddict
|
|
||||
|
|
||||
private async Task SeedOpenIddictAsync() |
|
||||
{ |
|
||||
await CreateScopeAsync("lingyun-abp-application"); |
|
||||
await CreateApplicationAsync("lingyun-abp-application"); |
|
||||
} |
|
||||
|
|
||||
private async Task CreateScopeAsync(string scope) |
|
||||
{ |
|
||||
if (await _scopeRepository.FindByNameAsync(scope) == null) |
|
||||
{ |
|
||||
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor() |
|
||||
{ |
|
||||
Name = scope, |
|
||||
DisplayName = scope + " access", |
|
||||
DisplayNames = |
|
||||
{ |
|
||||
[CultureInfo.GetCultureInfo("zh-Hans")] = "Abp API 应用程序访问", |
|
||||
[CultureInfo.GetCultureInfo("en")] = "Abp API Application Access" |
|
||||
}, |
|
||||
Resources = |
|
||||
{ |
|
||||
scope |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private async Task CreateApplicationAsync(string scope) |
|
||||
{ |
|
||||
var configurationSection = _configuration.GetSection("OpenIddict:Applications"); |
|
||||
|
|
||||
var vueClientId = configurationSection["VueAdmin:ClientId"]; |
|
||||
if (!vueClientId.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
var vueClientRootUrl = configurationSection["VueAdmin:RootUrl"].EnsureEndsWith('/'); |
|
||||
|
|
||||
if (await _applicationRepository.FindByClientIdAsync(vueClientId) == null) |
|
||||
{ |
|
||||
await _applicationManager.CreateAsync(new OpenIddictApplicationDescriptor |
|
||||
{ |
|
||||
ClientId = vueClientId, |
|
||||
ClientSecret = configurationSection["VueAdmin:ClientSecret"], |
|
||||
ApplicationType = OpenIddictConstants.ApplicationTypes.Web, |
|
||||
ConsentType = OpenIddictConstants.ConsentTypes.Explicit, |
|
||||
DisplayName = "Abp Vue Admin Client", |
|
||||
PostLogoutRedirectUris = |
|
||||
{ |
|
||||
new Uri(vueClientRootUrl + "signout-callback"), |
|
||||
new Uri(vueClientRootUrl) |
|
||||
}, |
|
||||
RedirectUris = |
|
||||
{ |
|
||||
new Uri(vueClientRootUrl + "signin-callback"), |
|
||||
new Uri(vueClientRootUrl) |
|
||||
}, |
|
||||
Permissions = |
|
||||
{ |
|
||||
OpenIddictConstants.Permissions.Endpoints.Authorization, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Token, |
|
||||
OpenIddictConstants.Permissions.Endpoints.DeviceAuthorization, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Introspection, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Revocation, |
|
||||
OpenIddictConstants.Permissions.Endpoints.EndSession, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.Implicit, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.Password, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.RefreshToken, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.DeviceCode, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.ClientCredentials, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.ResponseTypes.Code, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.IdToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.None, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.Token, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.Scopes.Roles, |
|
||||
OpenIddictConstants.Permissions.Scopes.Profile, |
|
||||
OpenIddictConstants.Permissions.Scopes.Email, |
|
||||
OpenIddictConstants.Permissions.Scopes.Address, |
|
||||
OpenIddictConstants.Permissions.Scopes.Phone, |
|
||||
OpenIddictConstants.Permissions.Prefixes.Scope + scope |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
var vueClientPermissions = new string[1] |
|
||||
{ |
|
||||
"AbpIdentity.UserLookup" |
|
||||
}; |
|
||||
await _permissionDataSeeder.SeedAsync(ClientPermissionValueProvider.ProviderName, vueClientId, vueClientPermissions); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
var internalServiceClientId = configurationSection["InternalService:ClientId"]; |
|
||||
if (!internalServiceClientId.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
if (await _applicationRepository.FindByClientIdAsync(internalServiceClientId) == null) |
|
||||
{ |
|
||||
await _applicationManager.CreateAsync(new OpenIddictApplicationDescriptor |
|
||||
{ |
|
||||
ClientId = internalServiceClientId, |
|
||||
ClientSecret = configurationSection["InternalService:ClientSecret"] ?? "1q2w3e*", |
|
||||
ClientType = OpenIddictConstants.ClientTypes.Confidential, |
|
||||
ConsentType = OpenIddictConstants.ConsentTypes.Explicit, |
|
||||
ApplicationType = OpenIddictConstants.ApplicationTypes.Native, |
|
||||
DisplayName = "Abp Vue Admin Client", |
|
||||
Permissions = |
|
||||
{ |
|
||||
OpenIddictConstants.Permissions.Endpoints.Authorization, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Token, |
|
||||
OpenIddictConstants.Permissions.Endpoints.DeviceAuthorization, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Introspection, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Revocation, |
|
||||
OpenIddictConstants.Permissions.Endpoints.EndSession, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.Implicit, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.Password, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.RefreshToken, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.DeviceCode, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.ClientCredentials, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.ResponseTypes.Code, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.IdToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.None, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.Token, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.Scopes.Roles, |
|
||||
OpenIddictConstants.Permissions.Scopes.Profile, |
|
||||
OpenIddictConstants.Permissions.Scopes.Email, |
|
||||
OpenIddictConstants.Permissions.Scopes.Address, |
|
||||
OpenIddictConstants.Permissions.Scopes.Phone, |
|
||||
OpenIddictConstants.Permissions.Prefixes.Scope + scope |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
var internalServicePermissions = new string[2] |
|
||||
{ |
|
||||
"AbpIdentity.UserLookup","AbpIdentity.Users" |
|
||||
}; |
|
||||
await _permissionDataSeeder.SeedAsync(ClientPermissionValueProvider.ProviderName, internalServiceClientId, internalServicePermissions); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
var oauthClientId = configurationSection["VueOAuthClient:ClientId"]; |
|
||||
if (!oauthClientId.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
var oauthClientRootUrls = configurationSection.GetSection("VueOAuthClient:RootUrls").Get<List<string>>(); |
|
||||
|
|
||||
if (await _applicationRepository.FindByClientIdAsync(oauthClientId) == null) |
|
||||
{ |
|
||||
var application = new OpenIddictApplicationDescriptor |
|
||||
{ |
|
||||
ClientId = oauthClientId, |
|
||||
ClientSecret = null, |
|
||||
ApplicationType = OpenIddictConstants.ApplicationTypes.Web, |
|
||||
ConsentType = OpenIddictConstants.ConsentTypes.Implicit, |
|
||||
DisplayName = "OAuth Client", |
|
||||
PostLogoutRedirectUris = { }, |
|
||||
RedirectUris = { }, |
|
||||
Permissions = |
|
||||
{ |
|
||||
OpenIddictConstants.Permissions.Endpoints.Authorization, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Token, |
|
||||
OpenIddictConstants.Permissions.Endpoints.DeviceAuthorization, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Introspection, |
|
||||
OpenIddictConstants.Permissions.Endpoints.Revocation, |
|
||||
OpenIddictConstants.Permissions.Endpoints.EndSession, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, |
|
||||
OpenIddictConstants.Permissions.GrantTypes.RefreshToken, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.ResponseTypes.Code, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.CodeToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.IdToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.None, |
|
||||
OpenIddictConstants.Permissions.ResponseTypes.Token, |
|
||||
|
|
||||
OpenIddictConstants.Permissions.Scopes.Roles, |
|
||||
OpenIddictConstants.Permissions.Scopes.Profile, |
|
||||
OpenIddictConstants.Permissions.Scopes.Email, |
|
||||
OpenIddictConstants.Permissions.Scopes.Address, |
|
||||
OpenIddictConstants.Permissions.Scopes.Phone, |
|
||||
OpenIddictConstants.Permissions.Prefixes.Scope + scope |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
oauthClientRootUrls.ForEach(url => |
|
||||
{ |
|
||||
application.PostLogoutRedirectUris.AddIfNotContains(new Uri(url.EnsureEndsWith('/'))); |
|
||||
application.PostLogoutRedirectUris.AddIfNotContains(new Uri(url.EnsureEndsWith('/') + "signout-callback")); |
|
||||
|
|
||||
application.RedirectUris.AddIfNotContains(new Uri(url)); |
|
||||
application.RedirectUris.AddIfNotContains(new Uri(url.EnsureEndsWith('/') + "signin-callback")); |
|
||||
application.RedirectUris.AddIfNotContains(new Uri(url.EnsureEndsWith('/') + "swagger/oauth2-redirect.html")); |
|
||||
}); |
|
||||
|
|
||||
await _applicationManager.CreateAsync(application); |
|
||||
|
|
||||
var oauthClientPermissions = new string[1] |
|
||||
{ |
|
||||
"AbpIdentity.UserLookup" |
|
||||
}; |
|
||||
await _permissionDataSeeder.SeedAsync(ClientPermissionValueProvider.ProviderName, oauthClientId, oauthClientPermissions); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endregion
|
|
||||
} |
|
||||
@ -0,0 +1,178 @@ |
|||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using OpenIddict.Abstractions; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.OpenIddict; |
||||
|
using Volo.Abp.OpenIddict.Applications; |
||||
|
using Volo.Abp.OpenIddict.Scopes; |
||||
|
using Volo.Abp.PermissionManagement; |
||||
|
|
||||
|
namespace LY.MicroService.Applications.Single.EntityFrameworkCore.DataSeeder; |
||||
|
public class OpenIddictDataSeederContributor : OpenIddictDataSeedContributorBase, IDataSeedContributor, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<OpenIddictDataSeederContributor> Logger { protected get; set; } |
||||
|
|
||||
|
protected IPermissionDataSeeder PermissionDataSeeder { get; } |
||||
|
public OpenIddictDataSeederContributor( |
||||
|
IConfiguration configuration, |
||||
|
IOpenIddictApplicationRepository openIddictApplicationRepository, |
||||
|
IAbpApplicationManager applicationManager, |
||||
|
IOpenIddictScopeRepository openIddictScopeRepository, |
||||
|
IOpenIddictScopeManager scopeManager, |
||||
|
IPermissionDataSeeder permissionDataSeeder) |
||||
|
: base(configuration, openIddictApplicationRepository, applicationManager, openIddictScopeRepository, scopeManager) |
||||
|
{ |
||||
|
PermissionDataSeeder = permissionDataSeeder; |
||||
|
|
||||
|
Logger = NullLogger<OpenIddictDataSeederContributor>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task SeedAsync(DataSeedContext context) |
||||
|
{ |
||||
|
var scope = "lingyun-abp-application"; |
||||
|
|
||||
|
Logger.LogInformation("Seeding the default scope..."); |
||||
|
await CreateScopeAsync(scope); |
||||
|
|
||||
|
Logger.LogInformation("Seeding the default applications..."); |
||||
|
await CreateApplicationAsync(scope); |
||||
|
} |
||||
|
|
||||
|
private async Task CreateScopeAsync(string scope) |
||||
|
{ |
||||
|
await CreateScopesAsync(new OpenIddictScopeDescriptor |
||||
|
{ |
||||
|
Name = scope, |
||||
|
DisplayName = scope + " access", |
||||
|
DisplayNames = |
||||
|
{ |
||||
|
[CultureInfo.GetCultureInfo("zh-Hans")] = "Abp API 应用程序访问", |
||||
|
[CultureInfo.GetCultureInfo("en")] = "Abp API Application Access" |
||||
|
}, |
||||
|
Resources = |
||||
|
{ |
||||
|
scope |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private async Task CreateApplicationAsync(string scope) |
||||
|
{ |
||||
|
var configurationSection = Configuration.GetSection("OpenIddict:Applications"); |
||||
|
var vueClientId = configurationSection["VueAdmin:ClientId"]; |
||||
|
if (!vueClientId.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
Logger.LogInformation("Seeding application {vueClientId}...", vueClientId); |
||||
|
|
||||
|
var vueClientRootUrls = configurationSection.GetSection("VueAdmin:RootUrls").Get<List<string>>() ?? []; |
||||
|
|
||||
|
var vueClientRedirectUrls = new List<string>(); |
||||
|
var vueClientPostLogoutRedirectUrls = new List<string>(); |
||||
|
vueClientRootUrls.ForEach(url => |
||||
|
{ |
||||
|
vueClientRedirectUrls.Add(url.EnsureEndsWith('/')); |
||||
|
vueClientRedirectUrls.Add(url.EnsureEndsWith('/') + "signin-callback"); |
||||
|
|
||||
|
vueClientPostLogoutRedirectUrls.Add(url.EnsureEndsWith('/')); |
||||
|
vueClientPostLogoutRedirectUrls.Add(url.EnsureEndsWith('/') + "signout-callback"); |
||||
|
}); |
||||
|
|
||||
|
await CreateOrUpdateApplicationAsync( |
||||
|
OpenIddictConstants.ApplicationTypes.Web, |
||||
|
vueClientId, |
||||
|
OpenIddictConstants.ClientTypes.Confidential, |
||||
|
OpenIddictConstants.ConsentTypes.Explicit, |
||||
|
"Abp Vue Admin Client", |
||||
|
configurationSection["VueAdmin:ClientSecret"] ?? "1q2w3e*", |
||||
|
[OpenIddictConstants.GrantTypes.AuthorizationCode, |
||||
|
OpenIddictConstants.GrantTypes.Implicit, |
||||
|
OpenIddictConstants.GrantTypes.Password, |
||||
|
OpenIddictConstants.GrantTypes.RefreshToken], |
||||
|
[OpenIddictConstants.Scopes.OpenId, |
||||
|
OpenIddictConstants.Scopes.Email, |
||||
|
OpenIddictConstants.Scopes.Roles, |
||||
|
OpenIddictConstants.Scopes.Address, |
||||
|
OpenIddictConstants.Scopes.Phone, |
||||
|
OpenIddictConstants.Scopes.Profile, |
||||
|
OpenIddictConstants.Scopes.OfflineAccess, |
||||
|
scope], |
||||
|
vueClientRedirectUrls, |
||||
|
vueClientPostLogoutRedirectUrls); |
||||
|
|
||||
|
var vueClientPermissions = new string[1] |
||||
|
{ |
||||
|
"AbpIdentity.UserLookup" |
||||
|
}; |
||||
|
await PermissionDataSeeder.SeedAsync(ClientPermissionValueProvider.ProviderName, vueClientId, vueClientPermissions); |
||||
|
} |
||||
|
|
||||
|
var internalServiceClientId = configurationSection["InternalService:ClientId"]; |
||||
|
if (!internalServiceClientId.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
Logger.LogInformation("Seeding application {internalServiceClientId}...", internalServiceClientId); |
||||
|
|
||||
|
await CreateOrUpdateApplicationAsync( |
||||
|
OpenIddictConstants.ApplicationTypes.Native, |
||||
|
internalServiceClientId, |
||||
|
OpenIddictConstants.ClientTypes.Confidential, |
||||
|
OpenIddictConstants.ConsentTypes.Explicit, |
||||
|
"Abp Internal Service Client", |
||||
|
configurationSection["InternalService:ClientSecret"] ?? "1q2w3e*", |
||||
|
[OpenIddictConstants.GrantTypes.ClientCredentials], |
||||
|
[OpenIddictConstants.ResponseTypes.Token, scope]); |
||||
|
} |
||||
|
|
||||
|
var oauthClientId = configurationSection["VueOAuthClient:ClientId"]; |
||||
|
if (!oauthClientId.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
Logger.LogInformation("Seeding application {oauthClientId}...", oauthClientId); |
||||
|
|
||||
|
var oauthClientRootUrls = configurationSection.GetSection("VueOAuthClient:RootUrls").Get<List<string>>() ?? []; |
||||
|
|
||||
|
var oauthClientRedirectUrls = new List<string>(); |
||||
|
var oauthClientPostLogoutRedirectUrls = new List<string>(); |
||||
|
oauthClientRootUrls.ForEach(url => |
||||
|
{ |
||||
|
oauthClientRedirectUrls.Add(url.EnsureEndsWith('/')); |
||||
|
oauthClientRedirectUrls.Add(url.EnsureEndsWith('/') + "signin-callback"); |
||||
|
oauthClientRedirectUrls.Add(url.EnsureEndsWith('/') + "swagger/oauth2-redirect.html"); |
||||
|
|
||||
|
oauthClientPostLogoutRedirectUrls.Add(url.EnsureEndsWith('/')); |
||||
|
oauthClientPostLogoutRedirectUrls.Add(url.EnsureEndsWith('/') + "signout-callback"); |
||||
|
}); |
||||
|
|
||||
|
await CreateOrUpdateApplicationAsync( |
||||
|
OpenIddictConstants.ApplicationTypes.Web, |
||||
|
oauthClientId, |
||||
|
OpenIddictConstants.ClientTypes.Public, |
||||
|
OpenIddictConstants.ConsentTypes.Implicit, |
||||
|
"Abp OAuth Client", |
||||
|
null, |
||||
|
[OpenIddictConstants.GrantTypes.AuthorizationCode, |
||||
|
OpenIddictConstants.GrantTypes.RefreshToken], |
||||
|
[OpenIddictConstants.Scopes.OpenId, |
||||
|
OpenIddictConstants.Scopes.Email, |
||||
|
OpenIddictConstants.Scopes.Roles, |
||||
|
OpenIddictConstants.Scopes.Address, |
||||
|
OpenIddictConstants.Scopes.Phone, |
||||
|
OpenIddictConstants.Scopes.Profile, |
||||
|
OpenIddictConstants.Scopes.OfflineAccess, |
||||
|
scope], |
||||
|
oauthClientRedirectUrls, |
||||
|
oauthClientPostLogoutRedirectUrls); |
||||
|
|
||||
|
var oauthClientPermissions = new string[1] |
||||
|
{ |
||||
|
"AbpIdentity.UserLookup" |
||||
|
}; |
||||
|
await PermissionDataSeeder.SeedAsync(ClientPermissionValueProvider.ProviderName, oauthClientId, oauthClientPermissions); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Polly; |
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement; |
||||
|
public class LocalizationDynamicInitializer : ITransientDependency |
||||
|
{ |
||||
|
public ILogger<LocalizationDynamicInitializer> Logger { get; set; } |
||||
|
|
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
protected IOptions<AbpLocalizationManagementOptions> Options { get; } |
||||
|
protected IHostApplicationLifetime ApplicationLifetime { get; } |
||||
|
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
||||
|
protected IStaticLocalizationSaver StaticLocalizationSaver { get; } |
||||
|
|
||||
|
public LocalizationDynamicInitializer( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IOptions<AbpLocalizationManagementOptions> options, |
||||
|
ICancellationTokenProvider cancellationTokenProvider, |
||||
|
IStaticLocalizationSaver staticLocalizationSaver) |
||||
|
{ |
||||
|
|
||||
|
ServiceProvider = serviceProvider; |
||||
|
Options = options; |
||||
|
ApplicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>(); |
||||
|
CancellationTokenProvider = cancellationTokenProvider; |
||||
|
StaticLocalizationSaver = staticLocalizationSaver; |
||||
|
|
||||
|
Logger = NullLogger<LocalizationDynamicInitializer>.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticLocalizationsToDatabase) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
if (runInBackground) |
||||
|
{ |
||||
|
Task.Run(async () => |
||||
|
{ |
||||
|
if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) |
||||
|
{ |
||||
|
cancellationToken = ApplicationLifetime.ApplicationStopping; |
||||
|
} |
||||
|
await ExecuteInitializationAsync(cancellationToken); |
||||
|
}, cancellationToken); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
return ExecuteInitializationAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
using (CancellationTokenProvider.Use(cancellationToken)) |
||||
|
{ |
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await SaveStaticLocalizationToDatabaseAsync(cancellationToken); |
||||
|
|
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
// No need to log here since inner calls log
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task SaveStaticLocalizationToDatabaseAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticLocalizationsToDatabase) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await Policy |
||||
|
.Handle<Exception>(e => e is not OperationCanceledException) |
||||
|
.WaitAndRetryAsync( |
||||
|
8, |
||||
|
retryAttempt => TimeSpan.FromSeconds( |
||||
|
Volo.Abp.RandomHelper.GetRandom( |
||||
|
(int)Math.Pow(2, retryAttempt) * 8, |
||||
|
(int)Math.Pow(2, retryAttempt) * 12) |
||||
|
) |
||||
|
) |
||||
|
.ExecuteAsync(async _ => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
|
||||
|
await StaticLocalizationSaver.SaveAsync(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
throw; // Polly will catch it
|
||||
|
} |
||||
|
}, cancellationToken); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,145 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Polly; |
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications; |
||||
|
public class NotificationDynamicInitializer : ITransientDependency |
||||
|
{ |
||||
|
public ILogger<NotificationDynamicInitializer> Logger { get; set; } |
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
protected IOptions<AbpNotificationsManagementOptions> Options { get; } |
||||
|
protected IHostApplicationLifetime ApplicationLifetime { get; } |
||||
|
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
||||
|
protected IDynamicNotificationDefinitionStore DynamicNotificationDefinitionStore { get; } |
||||
|
protected IStaticNotificationSaver StaticNotificationSaver { get; } |
||||
|
|
||||
|
public NotificationDynamicInitializer( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IOptions<AbpNotificationsManagementOptions> options, |
||||
|
ICancellationTokenProvider cancellationTokenProvider, |
||||
|
IDynamicNotificationDefinitionStore dynamicNotificationDefinitionStore, |
||||
|
IStaticNotificationSaver staticNotificationSaver) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
Options = options; |
||||
|
CancellationTokenProvider = cancellationTokenProvider; |
||||
|
DynamicNotificationDefinitionStore = dynamicNotificationDefinitionStore; |
||||
|
StaticNotificationSaver = staticNotificationSaver; |
||||
|
ApplicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>(); |
||||
|
|
||||
|
Logger = NullLogger<NotificationDynamicInitializer>.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticNotificationsToDatabase && !Options.Value.IsDynamicNotificationsStoreEnabled) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
if (runInBackground) |
||||
|
{ |
||||
|
Task.Run(async () => |
||||
|
{ |
||||
|
if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) |
||||
|
{ |
||||
|
cancellationToken = ApplicationLifetime.ApplicationStopping; |
||||
|
} |
||||
|
await ExecuteInitializationAsync(cancellationToken); |
||||
|
}, cancellationToken); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
return ExecuteInitializationAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
using (CancellationTokenProvider.Use(cancellationToken)) |
||||
|
{ |
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await SaveStaticNotificationssToDatabaseAsync(cancellationToken); |
||||
|
|
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await PreCacheDynamicNotificationsAsync(cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
// No need to log here since inner calls log
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task SaveStaticNotificationssToDatabaseAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticNotificationsToDatabase) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await Policy |
||||
|
.Handle<Exception>(e => e is not OperationCanceledException) |
||||
|
.WaitAndRetryAsync( |
||||
|
8, |
||||
|
retryAttempt => TimeSpan.FromSeconds( |
||||
|
Volo.Abp.RandomHelper.GetRandom( |
||||
|
(int)Math.Pow(2, retryAttempt) * 8, |
||||
|
(int)Math.Pow(2, retryAttempt) * 12) |
||||
|
) |
||||
|
) |
||||
|
.ExecuteAsync(async _ => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
|
||||
|
await StaticNotificationSaver.SaveAsync(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
throw; // Polly will catch it
|
||||
|
} |
||||
|
}, cancellationToken); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task PreCacheDynamicNotificationsAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (!Options.Value.IsDynamicNotificationsStoreEnabled) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
// Pre-cache notifications, so first request doesn't wait
|
||||
|
await DynamicNotificationDefinitionStore.GetGroupsAsync(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
throw; // It will be cached in Initialize()
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,115 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Hosting; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Polly; |
|
||||
using System; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Threading; |
|
||||
|
|
||||
namespace LINGYUN.Abp.TextTemplating; |
|
||||
public class TextTemplateDefinitionInitializer : ITransientDependency |
|
||||
{ |
|
||||
protected IRootServiceProvider RootServiceProvider { get; } |
|
||||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|
||||
protected AbpTextTemplatingCachingOptions TextTemplatingCachingOptions { get; } |
|
||||
|
|
||||
public TextTemplateDefinitionInitializer( |
|
||||
IRootServiceProvider rootServiceProvider, |
|
||||
ICancellationTokenProvider cancellationTokenProvider, |
|
||||
IOptions<AbpTextTemplatingCachingOptions> textTemplatingCachingOptions) |
|
||||
{ |
|
||||
RootServiceProvider = rootServiceProvider; |
|
||||
CancellationTokenProvider = cancellationTokenProvider; |
|
||||
TextTemplatingCachingOptions = textTemplatingCachingOptions.Value; |
|
||||
} |
|
||||
|
|
||||
public async virtual Task InitializeDynamicTemplates(CancellationToken cancellationToken) |
|
||||
{ |
|
||||
if (!TextTemplatingCachingOptions.SaveStaticTemplateDefinitionToDatabase && !TextTemplatingCachingOptions.IsDynamicTemplateDefinitionStoreEnabled) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
using var scope = RootServiceProvider.CreateScope(); |
|
||||
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>(); |
|
||||
var token = applicationLifetime?.ApplicationStopping ?? cancellationToken; |
|
||||
using (CancellationTokenProvider.Use(token)) |
|
||||
{ |
|
||||
if (CancellationTokenProvider.Token.IsCancellationRequested) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
await SaveStaticTemplateDefinitionsToDatabaseAsync(scope); |
|
||||
|
|
||||
if (CancellationTokenProvider.Token.IsCancellationRequested) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
await PreCacheDynamicTemplateDefinitionsAsync(scope); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private async Task SaveStaticTemplateDefinitionsToDatabaseAsync(IServiceScope serviceScope) |
|
||||
{ |
|
||||
if (!TextTemplatingCachingOptions.SaveStaticTemplateDefinitionToDatabase) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
await Policy |
|
||||
.Handle<Exception>() |
|
||||
.WaitAndRetryAsync(8, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 10)) |
|
||||
.ExecuteAsync(async _ => |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
// ReSharper disable once AccessToDisposedClosure
|
|
||||
var saver = serviceScope.ServiceProvider.GetRequiredService<IStaticTemplateSaver>(); |
|
||||
|
|
||||
await saver.SaveDefinitionTemplateAsync(); |
|
||||
|
|
||||
await saver.SaveTemplateContentAsync(); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
// ReSharper disable once AccessToDisposedClosure
|
|
||||
serviceScope.ServiceProvider |
|
||||
.GetService<ILogger<TextTemplateDefinitionInitializer>>()? |
|
||||
.LogException(ex); |
|
||||
|
|
||||
throw; // Polly will catch it
|
|
||||
} |
|
||||
}, CancellationTokenProvider.Token); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
private async Task PreCacheDynamicTemplateDefinitionsAsync(IServiceScope serviceScope) |
|
||||
{ |
|
||||
if (!TextTemplatingCachingOptions.IsDynamicTemplateDefinitionStoreEnabled) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
// ReSharper disable once AccessToDisposedClosure
|
|
||||
var store = serviceScope.ServiceProvider.GetRequiredService<ITemplateDefinitionStore>(); |
|
||||
|
|
||||
await store.GetAllAsync(); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
// ReSharper disable once AccessToDisposedClosure
|
|
||||
serviceScope.ServiceProvider |
|
||||
.GetService<ILogger<TextTemplateDefinitionInitializer>>()? |
|
||||
.LogException(ex); |
|
||||
|
|
||||
throw; // Polly will catch it
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,148 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Polly; |
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.TextTemplating; |
||||
|
public class TextTemplateDynamicInitializer : ITransientDependency |
||||
|
{ |
||||
|
public ILogger<TextTemplateDynamicInitializer> Logger { get; set; } |
||||
|
|
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
protected IOptions<AbpTextTemplatingCachingOptions> Options { get; } |
||||
|
protected IHostApplicationLifetime ApplicationLifetime { get; } |
||||
|
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
||||
|
protected ITemplateDefinitionStore TemplateDefinitionStore { get; } |
||||
|
protected IStaticTemplateSaver StaticTemplateSaver { get; } |
||||
|
|
||||
|
public TextTemplateDynamicInitializer( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IOptions<AbpTextTemplatingCachingOptions> options, |
||||
|
ICancellationTokenProvider cancellationTokenProvider, |
||||
|
ITemplateDefinitionStore templateDefinitionStore, |
||||
|
IStaticTemplateSaver staticTemplateSaver) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
Options = options; |
||||
|
ApplicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>(); |
||||
|
CancellationTokenProvider = cancellationTokenProvider; |
||||
|
TemplateDefinitionStore = templateDefinitionStore; |
||||
|
StaticTemplateSaver = staticTemplateSaver; |
||||
|
|
||||
|
Logger = NullLogger<TextTemplateDynamicInitializer>.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticTemplateDefinitionToDatabase && !Options.Value.IsDynamicTemplateDefinitionStoreEnabled) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
if (runInBackground) |
||||
|
{ |
||||
|
Task.Run(async () => |
||||
|
{ |
||||
|
if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) |
||||
|
{ |
||||
|
cancellationToken = ApplicationLifetime.ApplicationStopping; |
||||
|
} |
||||
|
await ExecuteInitializationAsync(cancellationToken); |
||||
|
}, cancellationToken); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
return ExecuteInitializationAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
using (CancellationTokenProvider.Use(cancellationToken)) |
||||
|
{ |
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await SaveStaticTextTemplatesToDatabaseAsync(cancellationToken); |
||||
|
|
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await PreCacheDynamicTextTemplatesAsync(cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
// No need to log here since inner calls log
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task SaveStaticTextTemplatesToDatabaseAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticTemplateDefinitionToDatabase) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await Policy |
||||
|
.Handle<Exception>(e => e is not OperationCanceledException) |
||||
|
.WaitAndRetryAsync( |
||||
|
8, |
||||
|
retryAttempt => TimeSpan.FromSeconds( |
||||
|
Volo.Abp.RandomHelper.GetRandom( |
||||
|
(int)Math.Pow(2, retryAttempt) * 8, |
||||
|
(int)Math.Pow(2, retryAttempt) * 12) |
||||
|
) |
||||
|
) |
||||
|
.ExecuteAsync(async _ => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
|
||||
|
await StaticTemplateSaver.SaveDefinitionTemplateAsync(); |
||||
|
await StaticTemplateSaver.SaveTemplateContentAsync(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
throw; // Polly will catch it
|
||||
|
} |
||||
|
}, cancellationToken); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task PreCacheDynamicTextTemplatesAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (!Options.Value.IsDynamicTemplateDefinitionStoreEnabled) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
|
||||
|
// Pre-cache tempte definitions, so first request doesn't wait
|
||||
|
await TemplateDefinitionStore.GetAllAsync(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
throw; // It will be cached in Initialize()
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,94 +0,0 @@ |
|||||
using LINGYUN.Abp.Webhooks; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Hosting; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using System; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Threading; |
|
||||
using Volo.Abp.Uow; |
|
||||
|
|
||||
namespace LINGYUN.Abp.WebhooksManagement; |
|
||||
public class WebhookDefinitionInitializer : ITransientDependency |
|
||||
{ |
|
||||
protected IRootServiceProvider RootServiceProvider { get; } |
|
||||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|
||||
protected WebhooksManagementOptions WebhooksManagementOptions { get; } |
|
||||
public WebhookDefinitionInitializer( |
|
||||
IRootServiceProvider rootServiceProvider, |
|
||||
ICancellationTokenProvider cancellationTokenProvider, |
|
||||
IOptions<WebhooksManagementOptions> webhooksManagementOptions) |
|
||||
{ |
|
||||
RootServiceProvider = rootServiceProvider; |
|
||||
CancellationTokenProvider = cancellationTokenProvider; |
|
||||
WebhooksManagementOptions = webhooksManagementOptions.Value; |
|
||||
} |
|
||||
|
|
||||
[UnitOfWork] |
|
||||
public async virtual Task InitializeDynamicWebhooks(CancellationToken cancellationToken) |
|
||||
{ |
|
||||
if (!WebhooksManagementOptions.SaveStaticWebhooksToDatabase && !WebhooksManagementOptions.IsDynamicWebhookStoreEnabled) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
using var scope = RootServiceProvider.CreateScope(); |
|
||||
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>(); |
|
||||
var token = applicationLifetime?.ApplicationStopping ?? cancellationToken; |
|
||||
try |
|
||||
{ |
|
||||
using (CancellationTokenProvider.Use(cancellationToken)) |
|
||||
{ |
|
||||
if (CancellationTokenProvider.Token.IsCancellationRequested) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
await SaveStaticNotificationsToDatabaseAsync(scope); |
|
||||
|
|
||||
if (CancellationTokenProvider.Token.IsCancellationRequested) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
await PreCacheDynamicNotificationsAsync(scope); |
|
||||
} |
|
||||
} |
|
||||
catch (OperationCanceledException) |
|
||||
{ |
|
||||
// ignore
|
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
scope.ServiceProvider |
|
||||
.GetService<ILogger<WebhookDefinitionInitializer>>()? |
|
||||
.LogException(ex); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private async Task SaveStaticNotificationsToDatabaseAsync(IServiceScope serviceScope) |
|
||||
{ |
|
||||
if (!WebhooksManagementOptions.SaveStaticWebhooksToDatabase) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var saver = serviceScope.ServiceProvider.GetRequiredService<IStaticWebhookSaver>(); |
|
||||
|
|
||||
await saver.SaveAsync(); |
|
||||
} |
|
||||
|
|
||||
private async Task PreCacheDynamicNotificationsAsync(IServiceScope serviceScope) |
|
||||
{ |
|
||||
if (!WebhooksManagementOptions.IsDynamicWebhookStoreEnabled) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var store = serviceScope.ServiceProvider.GetRequiredService<IDynamicWebhookDefinitionStore>(); |
|
||||
|
|
||||
await store.GetGroupsAsync(); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,150 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using LINGYUN.Abp.Webhooks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Polly; |
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.WebhooksManagement; |
||||
|
public class WebhookDynamicInitializer : ITransientDependency |
||||
|
{ |
||||
|
public ILogger<WebhookDynamicInitializer> Logger { get; set; } |
||||
|
|
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
protected IOptions<WebhooksManagementOptions> Options { get; } |
||||
|
[CanBeNull] |
||||
|
protected IHostApplicationLifetime ApplicationLifetime { get; } |
||||
|
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
||||
|
protected IDynamicWebhookDefinitionStore DynamicWebhookDefinitionStore { get; } |
||||
|
protected IStaticWebhookSaver StaticWebhookSaver { get; } |
||||
|
|
||||
|
public WebhookDynamicInitializer( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IOptions<WebhooksManagementOptions> options, |
||||
|
ICancellationTokenProvider cancellationTokenProvider, |
||||
|
IDynamicWebhookDefinitionStore dynamicWebhookDefinitionStore, |
||||
|
IStaticWebhookSaver staticWebhookSaver) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
Options = options; |
||||
|
ApplicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>(); |
||||
|
CancellationTokenProvider = cancellationTokenProvider; |
||||
|
DynamicWebhookDefinitionStore = dynamicWebhookDefinitionStore; |
||||
|
StaticWebhookSaver = staticWebhookSaver; |
||||
|
|
||||
|
Logger = NullLogger<WebhookDynamicInitializer>.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticWebhooksToDatabase && !Options.Value.IsDynamicWebhookStoreEnabled) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
if (runInBackground) |
||||
|
{ |
||||
|
Task.Run(async () => |
||||
|
{ |
||||
|
if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) |
||||
|
{ |
||||
|
cancellationToken = ApplicationLifetime.ApplicationStopping; |
||||
|
} |
||||
|
await ExecuteInitializationAsync(cancellationToken); |
||||
|
}, cancellationToken); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
return ExecuteInitializationAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task ExecuteInitializationAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
using (CancellationTokenProvider.Use(cancellationToken)) |
||||
|
{ |
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await SaveStaticWebhooksToDatabaseAsync(cancellationToken); |
||||
|
|
||||
|
if (CancellationTokenProvider.Token.IsCancellationRequested) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await PreCacheDynamicWebhooksAsync(cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
// No need to log here since inner calls log
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task SaveStaticWebhooksToDatabaseAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (!Options.Value.SaveStaticWebhooksToDatabase) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
await Policy |
||||
|
.Handle<Exception>(e => e is not OperationCanceledException) |
||||
|
.WaitAndRetryAsync( |
||||
|
8, |
||||
|
retryAttempt => TimeSpan.FromSeconds( |
||||
|
Volo.Abp.RandomHelper.GetRandom( |
||||
|
(int)Math.Pow(2, retryAttempt) * 8, |
||||
|
(int)Math.Pow(2, retryAttempt) * 12) |
||||
|
) |
||||
|
) |
||||
|
.ExecuteAsync(async _ => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
|
||||
|
await StaticWebhookSaver.SaveAsync(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
throw; // Polly will catch it
|
||||
|
} |
||||
|
}, cancellationToken); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task PreCacheDynamicWebhooksAsync(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (!Options.Value.IsDynamicWebhookStoreEnabled) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||
|
|
||||
|
// Pre-cache webhoks, so first request doesn't wait
|
||||
|
await DynamicWebhookDefinitionStore.GetGroupsAsync(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
throw; // It will be cached in Initialize()
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,9 +1,9 @@ |
|||||
{ |
{ |
||||
"version": "9.3.5", |
"version": "10.0.2", |
||||
"name": "my-app-single", |
"name": "my-app-single", |
||||
"private": true, |
"private": true, |
||||
"dependencies": { |
"dependencies": { |
||||
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "4.3.5", |
"@abp/aspnetcore.mvc.ui.theme.leptonxlite": "5.0.2", |
||||
"@abp/qrcode": "9.3.5" |
"@abp/qrcode": "10.0.2" |
||||
} |
} |
||||
} |
} |
||||
Loading…
Reference in new issue