12 changed files with 317 additions and 2 deletions
@ -0,0 +1,48 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.DbMigrator.Keycloak; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Serilog; |
|||
using Volo.Abp; |
|||
|
|||
namespace EShopOnAbp.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 = AbpApplicationFactory.Create<EShopOnAbpDbMigratorModule>(options => |
|||
{ |
|||
options.Services.ReplaceConfiguration(_configuration); |
|||
options.UseAutofac(); |
|||
options.Services.AddLogging(c => c.AddSerilog()); |
|||
})) |
|||
{ |
|||
application.Initialize(); |
|||
|
|||
await application |
|||
.ServiceProvider |
|||
.GetRequiredService<MigrationService>() |
|||
.MigrateAsync(cancellationToken); |
|||
|
|||
application.Shutdown(); |
|||
|
|||
_hostApplicationLifetime.StopApplication(); |
|||
} |
|||
} |
|||
|
|||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace>EShopOnAbp.DbMigrator</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" /> |
|||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" /> |
|||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\EShopOnAbp.Shared.Hosting\EShopOnAbp.Shared.Hosting.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Compile Remove="Logs\**" /> |
|||
<Content Remove="Logs\**" /> |
|||
<EmbeddedResource Remove="Logs\**" /> |
|||
<None Remove="Logs\**" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="appsettings.json" /> |
|||
<Content Include="appsettings.json"> |
|||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> |
|||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
|||
</Content> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,21 @@ |
|||
using EShopOnAbp.DbMigrator.Keycloak; |
|||
using EShopOnAbp.Shared.Hosting; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EShopOnAbp.DbMigrator; |
|||
|
|||
[DependsOn( |
|||
typeof(EShopOnAbpSharedHostingModule) |
|||
)] |
|||
public class EShopOnAbpDbMigratorModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
|
|||
context.Services.AddHttpClient(KeycloakService.HttpClientName); |
|||
|
|||
Configure<KeycloakClientOptions>(configuration); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.DbMigrator.Keycloak; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.DbMigrator; |
|||
|
|||
public class KeyCloakDataSeeder : IDataSeedContributor, ITransientDependency |
|||
{ |
|||
private readonly KeycloakService _keycloakService; |
|||
|
|||
public KeyCloakDataSeeder(KeycloakService keycloakService) |
|||
{ |
|||
_keycloakService = keycloakService; |
|||
} |
|||
|
|||
public async Task SeedAsync(DataSeedContext context) |
|||
{ |
|||
var result = await _keycloakService.GetAdminAccessTokenAsync("master"); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace EShopOnAbp.DbMigrator.Keycloak; |
|||
|
|||
public class KeycloakClientOptions |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Net.Http.Headers; |
|||
using System.Net.Http.Json; |
|||
using System.Text; |
|||
using System.Text.Json; |
|||
using System.Text.Json.Serialization; |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.DbMigrator.Keycloak.Models; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.DbMigrator.Keycloak; |
|||
|
|||
public class KeycloakService : ITransientDependency |
|||
{ |
|||
public const string HttpClientName = "KeycloakServiceHttpClientName"; |
|||
|
|||
// TODO: Option
|
|||
public const string BaseUrl = "http://localhost:8080/admin/realms"; |
|||
public const string AdminClientId = "admin-cli"; |
|||
public const string AdminUserName = "admin"; |
|||
public const string AdminPassword = "1q2w3E*"; |
|||
|
|||
private readonly ILogger<KeycloakService> _logger; |
|||
private readonly IHttpClientFactory _clientFactory; |
|||
|
|||
public KeycloakService(IHttpClientFactory clientFactory, ILogger<KeycloakService> logger) |
|||
{ |
|||
_clientFactory = clientFactory; |
|||
_logger = logger; |
|||
} |
|||
|
|||
// public async Task<string> CreateClientAsync(string realm, Client client)
|
|||
// {
|
|||
// HttpResponseMessage response = await InternalCreateClientAsync(realm, client).ConfigureAwait(false);
|
|||
//
|
|||
// var locationPathAndQuery = response.Headers.Location.PathAndQuery;
|
|||
// var clientId = response.IsSuccessStatusCode
|
|||
// ? locationPathAndQuery.Substring(locationPathAndQuery.LastIndexOf("/", StringComparison.Ordinal) + 1)
|
|||
// : null;
|
|||
// return clientId;
|
|||
// }
|
|||
|
|||
private HttpClient CreateKeycloakApiHttpClient(string realm) |
|||
{ |
|||
var httpClient = _clientFactory.CreateClient(HttpClientName); |
|||
httpClient.BaseAddress = new Uri(BaseUrl); |
|||
|
|||
return httpClient; |
|||
} |
|||
|
|||
private async Task<HttpClient> CreateKeycloakApiHttpClientAsync(string realm, string token = null) |
|||
{ |
|||
var httpClient = CreateKeycloakApiHttpClient(realm); |
|||
httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); |
|||
if (!string.IsNullOrEmpty(token)) |
|||
{ |
|||
var accessToken = await GetAdminAccessTokenAsync(realm); |
|||
httpClient.DefaultRequestHeaders.Authorization = |
|||
new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"{accessToken}"); |
|||
} |
|||
|
|||
return httpClient; |
|||
} |
|||
|
|||
public async Task<string> GetAdminAccessTokenAsync(string realm) |
|||
{ |
|||
var httpClient = _clientFactory.CreateClient(HttpClientName); |
|||
httpClient.BaseAddress = new Uri(BaseUrl); |
|||
|
|||
var result = string.Empty; |
|||
|
|||
var formContent = new FormUrlEncodedContent(new[] |
|||
{ |
|||
new KeyValuePair<string, string>("client_id", AdminClientId), |
|||
new KeyValuePair<string, string>("username", AdminUserName), |
|||
new KeyValuePair<string, string>("password", AdminPassword), |
|||
new KeyValuePair<string, string>("grant_type", "password") |
|||
}); |
|||
|
|||
var httpResponseMessage = |
|||
await httpClient.PostAsync($"/realms/{realm}/protocol/openid-connect/token", formContent); |
|||
|
|||
if (httpResponseMessage.IsSuccessStatusCode) |
|||
{ |
|||
var response = await httpResponseMessage.Content.ReadFromJsonAsync<AccessTokenResult>(); |
|||
result = response?.AccessToken; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace EShopOnAbp.DbMigrator.Keycloak.Models; |
|||
|
|||
public class AccessTokenResult |
|||
{ |
|||
[JsonPropertyName("access_token")] public string AccessToken { get; set; } |
|||
[JsonPropertyName("expires_in")] public int Expiration { get; set; } |
|||
[JsonPropertyName("refresh_token")] public string RefreshToken { get; set; } |
|||
|
|||
[JsonPropertyName("refresh_expires_in")] |
|||
public int RefreshExpiration { get; set; } |
|||
|
|||
[JsonPropertyName("token_type")] public string TokenType { get; set; } |
|||
[JsonPropertyName("scope")] public string Scope { get; set; } |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.DbMigrator; |
|||
|
|||
public class MigrationService: ITransientDependency |
|||
{ |
|||
private readonly ILogger<MigrationService> _logger; |
|||
private readonly IDataSeeder _dataSeeder; |
|||
|
|||
public MigrationService(ILogger<MigrationService> logger, IDataSeeder dataSeeder) |
|||
{ |
|||
_logger = logger; |
|||
_dataSeeder = dataSeeder; |
|||
} |
|||
|
|||
public async Task MigrateAsync(CancellationToken cancellationToken) |
|||
{ |
|||
// Check if keycloak api is available
|
|||
|
|||
//Seed data
|
|||
await _dataSeeder.SeedAsync(); |
|||
|
|||
_logger.LogInformation("Migration completed!"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.DbMigrator; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
using Serilog; |
|||
using Serilog.Events; |
|||
|
|||
class Program |
|||
{ |
|||
async static Task Main(string[] args) |
|||
{ |
|||
Log.Logger = new LoggerConfiguration() |
|||
#if DEBUG
|
|||
.MinimumLevel.Debug() |
|||
#else
|
|||
.MinimumLevel.Information() |
|||
#endif
|
|||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information) |
|||
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) |
|||
.Enrich.FromLogContext() |
|||
.Enrich.WithProperty("Application", $"DbMigrator") |
|||
.WriteTo.Async(c => c.File("Logs/logs.txt")) |
|||
.WriteTo.Async(c => c.Console()) |
|||
.CreateLogger(); |
|||
|
|||
await CreateHostBuilder(args).RunConsoleAsync(); |
|||
} |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.AddAppSettingsSecretsJson() |
|||
.ConfigureLogging((context, logging) => logging.ClearProviders()) |
|||
.ConfigureServices((hostContext, services) => { services.AddHostedService<DbMigratorHostedService>(); }); |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
|
|||
} |
|||
Loading…
Reference in new issue