feat: 先搞一个dbmigrator凑合用

This commit is contained in:
2025-10-25 16:06:23 +08:00
parent ab8f120366
commit 283c30310a
17 changed files with 290 additions and 58 deletions

View File

@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TenantManagement;
namespace KonSoft.Shared.DbMigrator.Data;
public class DbMigrationService : ITransientDependency
{
private readonly ICurrentTenant _currentTenant;
private readonly IDataSeeder _dataSeeder;
private readonly IEnumerable<IDbSchemaMigrator> _dbSchemaMigrators;
private readonly ITenantRepository _tenantRepository;
public DbMigrationService(
IDataSeeder dataSeeder,
IEnumerable<IDbSchemaMigrator> dbSchemaMigrators,
ITenantRepository tenantRepository,
ICurrentTenant currentTenant)
{
_dataSeeder = dataSeeder;
_dbSchemaMigrators = dbSchemaMigrators;
_tenantRepository = tenantRepository;
_currentTenant = currentTenant;
Logger = NullLogger<DbMigrationService>.Instance;
}
public ILogger<DbMigrationService> Logger { get; set; }
public async Task MigrateAsync()
{
var initialMigrationAdded = AddInitialMigrationIfNotExist();
if (initialMigrationAdded)
{
return;
}
Logger.LogInformation("Started database migrations...");
await MigrateDatabaseSchemaAsync();
await SeedDataAsync();
Logger.LogInformation("Successfully completed host database migrations.");
var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
var migratedDatabaseSchemas = new HashSet<string>();
foreach (var tenant in tenants)
{
using (_currentTenant.Change(tenant.Id))
{
if (tenant.ConnectionStrings.Any())
{
var tenantConnectionStrings = tenant.ConnectionStrings
.Select(x => x.Value)
.ToList();
if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings))
{
await MigrateDatabaseSchemaAsync(tenant);
migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings);
}
}
await SeedDataAsync(tenant);
}
Logger.LogInformation($"Successfully completed {tenant.Name} tenant database migrations.");
}
Logger.LogInformation("Successfully completed all database migrations.");
Logger.LogInformation("You can safely end this process...");
}
private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null)
{
Logger.LogInformation(
$"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database...");
foreach (var migrator in _dbSchemaMigrators)
{
await migrator.MigrateAsync();
}
}
private async Task SeedDataAsync(Tenant? tenant = null)
{
Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed...");
await _dataSeeder.SeedAsync(new DataSeedContext(tenant?.Id)
.WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName,
IdentityDataSeedContributor.AdminEmailDefaultValue)
.WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName,
IdentityDataSeedContributor.AdminPasswordDefaultValue)
);
}
private bool AddInitialMigrationIfNotExist()
{
try
{
if (!DbMigrationsProjectExists())
{
return false;
}
}
catch (Exception)
{
return false;
}
try
{
if (!MigrationsFolderExists())
{
AddInitialMigration();
return true;
}
return false;
}
catch (Exception e)
{
Logger.LogWarning("Couldn't determinate if any migrations exist : " + e.Message);
return false;
}
}
private bool DbMigrationsProjectExists()
{
var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath();
return dbMigrationsProjectFolder != null;
}
private bool MigrationsFolderExists()
{
var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath();
return dbMigrationsProjectFolder != null &&
Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations"));
}
private void AddInitialMigration()
{
Logger.LogInformation("Creating initial migration...");
string argumentPrefix;
string fileName;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
argumentPrefix = "-c";
fileName = "/bin/bash";
}
else
{
argumentPrefix = "/C";
fileName = "cmd.exe";
}
var procStartInfo = new ProcessStartInfo(fileName,
$"{argumentPrefix} \"abp create-migration-and-run-migrator \"{GetEntityFrameworkCoreProjectFolderPath()}\"\""
);
try
{
Process.Start(procStartInfo);
}
catch (Exception)
{
throw new Exception("Couldn't run ABP CLI...");
}
}
private string? GetEntityFrameworkCoreProjectFolderPath()
{
var slnDirectoryPath = GetSolutionDirectoryPath();
if (slnDirectoryPath == null)
{
throw new Exception("Solution folder not found!");
}
var srcDirectoryPath = Path.Combine(slnDirectoryPath, "src");
return Directory.GetDirectories(srcDirectoryPath)
.FirstOrDefault(d => d.EndsWith(".EntityFrameworkCore"));
}
private string? GetSolutionDirectoryPath()
{
var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null)
{
currentDirectory = Directory.GetParent(currentDirectory.FullName);
if (currentDirectory != null &&
Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null)
{
return currentDirectory.FullName;
}
}
return null;
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Threading.Tasks;
using KonSoft.Admin.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace KonSoft.Shared.DbMigrator.Data;
public class EntityFrameworkCoreDbSchemaMigrator
: IDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreDbSchemaMigrator(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We intentionally resolve the AdminDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<AdminDbContext>()
.Database
.MigrateAsync();
}
}

View File

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace KonSoft.Shared.DbMigrator.Data;
public interface IDbSchemaMigrator
{
Task MigrateAsync();
}

View File

@ -0,0 +1,51 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System.Threading;
using System.Threading.Tasks;
using KonSoft.Shared.DbMigrator.Data;
using Volo.Abp;
using Volo.Abp.Data;
namespace KonSoft.Shared.DbMigrator;
public class DbMigratorHostedService : IHostedService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly IConfiguration _configuration;
public DbMigratorHostedService(IHostApplicationLifetime hostApplicationLifetime, IConfiguration configuration)
{
_hostApplicationLifetime = hostApplicationLifetime;
_configuration = configuration;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var application = await AbpApplicationFactory.CreateAsync<KonSoftSharedDbMigratorModule>(options =>
{
options.Services.ReplaceConfiguration(_configuration);
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
options.AddDataMigrationEnvironment();
}))
{
await application.InitializeAsync();
await application
.ServiceProvider
.GetRequiredService<DbMigrationService>()
.MigrateAsync();
await application.ShutdownAsync();
_hostApplicationLifetime.StopApplication();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,26 @@
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
USER $APP_UID
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
COPY ["./common.props", "."]
COPY ["./NuGet.Config", "."]
COPY ["./src/KonSoft.SnowClub.DbMigrator/KonSoft.SnowClub.DbMigrator.csproj", "KonSoft.SnowClub.DbMigrator/"]
COPY ["./src/KonSoft.SnowClub.Application.Contracts/KonSoft.SnowClub.Application.Contracts.csproj", "KonSoft.SnowClub.Application.Contracts/"]
COPY ["./src/KonSoft.SnowClub.Domain.Shared/KonSoft.SnowClub.Domain.Shared.csproj", "KonSoft.SnowClub.Domain.Shared/"]
COPY ["./src/KonSoft.SnowClub.EntityFrameworkCore/KonSoft.SnowClub.EntityFrameworkCore.csproj", "KonSoft.SnowClub.EntityFrameworkCore/"]
COPY ["./src/KonSoft.SnowClub.Domain/KonSoft.SnowClub.Domain.csproj", "KonSoft.SnowClub.Domain/"]
RUN dotnet restore "./KonSoft.SnowClub.DbMigrator/KonSoft.SnowClub.DbMigrator.csproj"
COPY . .
WORKDIR "/src/KonSoft.SnowClub.DbMigrator"
RUN dotnet build "./KonSoft.SnowClub.DbMigrator.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./KonSoft.SnowClub.DbMigrator.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "KonSoft.SnowClub.DbMigrator.dll"]

View File

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\modules\admin\src\KonSoft.Admin.Application.Contracts\KonSoft.Admin.Application.Contracts.csproj" />
<ProjectReference Include="..\..\modules\admin\src\KonSoft.Admin.EntityFrameworkCore\KonSoft.Admin.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\KonSoft.Shared.Hosting.AspNetCore\KonSoft.Shared.Hosting.AspNetCore.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Logs\**" />
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<None Remove="Logs\**" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,20 @@
using KonSoft.Admin;
using KonSoft.Admin.EntityFrameworkCore;
using KonSoft.Shared.Hosting;
using Volo.Abp.Caching;
using Volo.Abp.Modularity;
namespace KonSoft.Shared.DbMigrator;
[DependsOn(
typeof(KonSoftSharedHostingModule),
typeof(AdminEntityFrameworkCoreModule),
typeof(AdminApplicationContractsModule)
)]
public class KonSoftSharedDbMigratorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDistributedCacheOptions>(options => { options.KeyPrefix = "KonSoft:"; });
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KonSoft.Shared.DbMigrator.OpenIddict
{
public class OpenIddictApplicationOptions
{
public string Name { get; set; }
public string Type { get; set; }
public string ConsentType { get; set; }
public string DisplayName { get; set; }
public string? Secret { get; set; }
public List<string> GrantTypes { get; set; }
public string? PostLogoutRedirectUri { get; set; }
}
}

View File

@ -0,0 +1,364 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using OpenIddict.Abstractions;
using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.OpenIddict.Applications;
using Volo.Abp.OpenIddict.Scopes;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow;
namespace KonSoft.Shared.DbMigrator.OpenIddict;
/* Creates initial data that is needed to property run the application
* and make client-to-server communication possible.
*/
public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IAbpApplicationManager _applicationManager;
private readonly IConfiguration _configuration;
private readonly IOpenIddictApplicationRepository _openIddictApplicationRepository;
private readonly IOpenIddictScopeRepository _openIddictScopeRepository;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly IStringLocalizer<OpenIddictResponse> L;
public OpenIddictDataSeedContributor(
IConfiguration configuration,
IOpenIddictApplicationRepository openIddictApplicationRepository,
IAbpApplicationManager applicationManager,
IOpenIddictScopeRepository openIddictScopeRepository,
IOpenIddictScopeManager scopeManager,
IPermissionDataSeeder permissionDataSeeder,
IStringLocalizer<OpenIddictResponse> l)
{
_configuration = configuration;
_openIddictApplicationRepository = openIddictApplicationRepository;
_applicationManager = applicationManager;
_openIddictScopeRepository = openIddictScopeRepository;
_scopeManager = scopeManager;
_permissionDataSeeder = permissionDataSeeder;
L = l;
}
[UnitOfWork]
public virtual async Task SeedAsync(DataSeedContext context)
{
await CreateScopesAsync();
await CreateApplicationsAsync();
}
private async Task CreateScopesAsync()
{
if (await _openIddictScopeRepository.FindByNameAsync("Admin") == null)
{
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "Admin",
DisplayName = "Admin API",
Resources = { "Admin" }
});
}
if (await _openIddictScopeRepository.FindByNameAsync("Dispatch") == null)
{
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "Dispatch",
DisplayName = "Dispatch API",
Resources = { "Dispatch" }
});
}
if (await _openIddictScopeRepository.FindByNameAsync("Payment") == null)
{
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "Payment",
DisplayName = "Payment API",
Resources = { "Payment" }
});
}
if (await _openIddictScopeRepository.FindByNameAsync("Report") == null)
{
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "Report",
DisplayName = "Report API",
Resources = { "Report" }
});
}
if (await _openIddictScopeRepository.FindByNameAsync("TenantManagement") == null)
{
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "TenantManagement",
DisplayName = "TenantManagement API",
Resources = { "TenantManagement" }
});
}
}
private async Task CreateApplicationsAsync()
{
var commonScopes = new List<string>
{
OpenIddictConstants.Permissions.Scopes.Address,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Phone,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles,
"Admin",
"Dispatch",
"Payment",
"Report",
"TenantManagement"
};
var apps = new List<OpenIddictApplicationOptions>();
_configuration.GetSection("OpenIddict:Applications").Bind(apps);
foreach (var openIddictApplication in apps)
{
await CreateApplicationAsync(openIddictApplication.Name, openIddictApplication.Type,
openIddictApplication.ConsentType, openIddictApplication.DisplayName, openIddictApplication.Secret,
openIddictApplication.GrantTypes, commonScopes, openIddictApplication.PostLogoutRedirectUri);
}
}
private async Task CreateApplicationAsync(
[NotNull] string name,
[NotNull] string type,
[NotNull] string consentType,
string displayName,
string? secret,
List<string> grantTypes,
List<string> scopes,
string? clientUri = null,
string? redirectUri = null,
string? postLogoutRedirectUri = null,
List<string>? permissions = null)
{
if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public,
StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException(L["NoClientSecretCanBeSetForPublicApplications"]);
}
if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential,
StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException(L["TheClientSecretIsRequiredForConfidentialApplications"]);
}
var client = await _openIddictApplicationRepository.FindByClientIdAsync(name);
var application = new AbpApplicationDescriptor
{
ClientId = name,
ClientType = type,
ClientSecret = secret,
ConsentType = consentType,
DisplayName = displayName,
ClientUri = clientUri
};
Check.NotNullOrEmpty(grantTypes, nameof(grantTypes));
Check.NotNullOrEmpty(scopes, nameof(scopes));
if (new[] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }.All(
grantTypes.Contains))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken);
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken);
}
}
if (!redirectUri.IsNullOrWhiteSpace() || !postLogoutRedirectUri.IsNullOrWhiteSpace())
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout);
}
var buildInGrantTypes = new[]
{
OpenIddictConstants.GrantTypes.Implicit, OpenIddictConstants.GrantTypes.Password,
OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.ClientCredentials,
OpenIddictConstants.GrantTypes.DeviceCode, OpenIddictConstants.GrantTypes.RefreshToken
};
foreach (var grantType in grantTypes)
{
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code);
}
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization);
}
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
grantType == OpenIddictConstants.GrantTypes.ClientCredentials ||
grantType == OpenIddictConstants.GrantTypes.Password ||
grantType == OpenIddictConstants.GrantTypes.RefreshToken ||
grantType == OpenIddictConstants.GrantTypes.DeviceCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
}
if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials);
}
if (grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit);
}
if (grantType == OpenIddictConstants.GrantTypes.Password)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password);
}
if (grantType == OpenIddictConstants.GrantTypes.RefreshToken)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken);
}
if (grantType == OpenIddictConstants.GrantTypes.DeviceCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.DeviceCode);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Device);
}
if (grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken);
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token);
}
}
if (!buildInGrantTypes.Contains(grantType))
{
application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType);
}
}
var buildInScopes = new[]
{
OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles
};
foreach (var scope in scopes)
{
if (buildInScopes.Contains(scope))
{
application.Permissions.Add(scope);
}
else
{
application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope);
}
}
if (redirectUri != null)
{
if (!redirectUri.IsNullOrEmpty())
{
if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString())
{
throw new BusinessException(L["InvalidRedirectUri", redirectUri]);
}
if (application.RedirectUris.All(x => x != uri))
{
application.RedirectUris.Add(uri);
}
}
}
if (postLogoutRedirectUri != null)
{
if (!postLogoutRedirectUri.IsNullOrEmpty())
{
if (!Uri.TryCreate(postLogoutRedirectUri, UriKind.Absolute, out var uri) ||
!uri.IsWellFormedOriginalString())
{
throw new BusinessException(L["InvalidPostLogoutRedirectUri", postLogoutRedirectUri]);
}
if (application.PostLogoutRedirectUris.All(x => x != uri))
{
application.PostLogoutRedirectUris.Add(uri);
}
}
}
if (permissions != null)
{
await _permissionDataSeeder.SeedAsync(
ClientPermissionValueProvider.ProviderName,
name,
permissions
);
}
if (client == null)
{
await _applicationManager.CreateAsync(application);
return;
}
if (!HasSameRedirectUris(client, application))
{
client.RedirectUris =
JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/')));
client.PostLogoutRedirectUris =
JsonSerializer.Serialize(application.PostLogoutRedirectUris.Select(q => q.ToString().TrimEnd('/')));
await _applicationManager.UpdateAsync(client.ToModel());
}
if (!HasSameScopes(client, application))
{
client.Permissions = JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString()));
await _applicationManager.UpdateAsync(client.ToModel());
}
}
private bool HasSameRedirectUris(OpenIddictApplication existingClient, AbpApplicationDescriptor application)
{
return existingClient.RedirectUris ==
JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/')));
}
private bool HasSameScopes(OpenIddictApplication existingClient, AbpApplicationDescriptor application)
{
return existingClient.Permissions ==
JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString().TrimEnd('/')));
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
using System;
using System.Threading.Tasks;
namespace KonSoft.Shared.DbMigrator;
class Program
{
static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning)
#if DEBUG
.MinimumLevel.Override("KonSoft.Clean", LogEventLevel.Debug)
#else
.MinimumLevel.Override("KonSoft.Clean", LogEventLevel.Information)
#endif
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
await CreateHostBuilder(args).RunConsoleAsync();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAgileConfig(options =>
{
options.ENV = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development";
})
.AddAppSettingsSecretsJson()
.ConfigureLogging((context, logging) => logging.ClearProviders())
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<DbMigratorHostedService>();
});
}

View File

@ -0,0 +1,7 @@
{
"profiles": {
"KonSoft.SnowClub.DbMigrator": {
"commandName": "Project"
}
}
}

View File

@ -0,0 +1,8 @@
{
"AgileConfig": {
"appId": "KonSoft.Shared.DbMigrator",
"name": "KonSoft.Shared.DbMigrator",
"nodes": "https://config.konsoft.top/",
"secret": "DBE31703-14F9-4B01-893D-900B8380CE04"
}
}