45 changed files with 1403 additions and 2 deletions
@ -0,0 +1,99 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFramework>net6.0</TargetFramework> |
||||
<Nullable>enable</Nullable> |
||||
<ImplicitUsings>enable</ImplicitUsings> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" /> |
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Autofac" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Swashbuckle" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.EntityFrameworkCore.PostgreSql" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.Account.Application" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Account.HttpApi" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Account.Web.IdentityServer" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Identity" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Identity.Application" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Identity.HttpApi" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.IdentityServer.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.PermissionManagement.HttpApi" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.TenantManagement.Application" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.TenantManagement.HttpApi" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.Featuremanagement.Application" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Featuremanagement.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.Featuremanagement.HttpApi" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.SettingManagement.Application" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
<PackageReference Include="Volo.Abp.SettingManagement.HttpApi" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="$(VoloAbpVersion)" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.1" /> |
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.1" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1"> |
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |
||||
<PrivateAssets>compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native</PrivateAssets> |
||||
</PackageReference> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<Content Remove="Localization\BookStore\*.json" /> |
||||
<EmbeddedResource Include="Localization\BookStore\*.json" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<Compile Remove="Logs\**" /> |
||||
<Content Remove="Logs\**" /> |
||||
<EmbeddedResource Remove="Logs\**" /> |
||||
<None Remove="Logs\**" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\..\modules\mvc\Sanhe.Abp.AspNetCore.Mvc.Wrapper\Sanhe.Abp.AspNetCore.Mvc.Wrapper.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,345 @@
|
||||
using BookStore.Data; |
||||
using BookStore.Localization; |
||||
using Microsoft.AspNetCore.Cors; |
||||
using Microsoft.AspNetCore.DataProtection; |
||||
using Microsoft.OpenApi.Models; |
||||
using Sanhe.Abp.AspNetCore.Mvc.Wrapper; |
||||
using Sanhe.Abp.Wrapper; |
||||
using StackExchange.Redis; |
||||
using Volo.Abp; |
||||
using Volo.Abp.Account; |
||||
using Volo.Abp.Account.Web; |
||||
using Volo.Abp.AspNetCore.Authentication.JwtBearer; |
||||
using Volo.Abp.AspNetCore.MultiTenancy; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
using Volo.Abp.AspNetCore.Mvc.Localization; |
||||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; |
||||
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling; |
||||
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; |
||||
using Volo.Abp.AspNetCore.Serilog; |
||||
using Volo.Abp.AuditLogging.EntityFrameworkCore; |
||||
using Volo.Abp.Autofac; |
||||
using Volo.Abp.AutoMapper; |
||||
using Volo.Abp.Caching.StackExchangeRedis; |
||||
using Volo.Abp.EntityFrameworkCore; |
||||
using Volo.Abp.EntityFrameworkCore.PostgreSql; |
||||
using Volo.Abp.FeatureManagement; |
||||
using Volo.Abp.FeatureManagement.EntityFrameworkCore; |
||||
using Volo.Abp.Identity; |
||||
using Volo.Abp.Identity.EntityFrameworkCore; |
||||
using Volo.Abp.IdentityServer.EntityFrameworkCore; |
||||
using Volo.Abp.Localization; |
||||
using Volo.Abp.Localization.ExceptionHandling; |
||||
using Volo.Abp.Modularity; |
||||
using Volo.Abp.MultiTenancy; |
||||
using Volo.Abp.PermissionManagement; |
||||
using Volo.Abp.PermissionManagement.EntityFrameworkCore; |
||||
using Volo.Abp.PermissionManagement.HttpApi; |
||||
using Volo.Abp.PermissionManagement.Identity; |
||||
using Volo.Abp.SettingManagement; |
||||
using Volo.Abp.SettingManagement.EntityFrameworkCore; |
||||
using Volo.Abp.Swashbuckle; |
||||
using Volo.Abp.TenantManagement; |
||||
using Volo.Abp.TenantManagement.EntityFrameworkCore; |
||||
using Volo.Abp.UI.Navigation.Urls; |
||||
using Volo.Abp.Validation.Localization; |
||||
using Volo.Abp.VirtualFileSystem; |
||||
|
||||
namespace BookStore; |
||||
|
||||
[DependsOn( |
||||
// ABP Framework packages |
||||
typeof(AbpAspNetCoreMvcModule), |
||||
typeof(AbpAspNetCoreMultiTenancyModule), |
||||
typeof(AbpAutofacModule), |
||||
typeof(AbpAutoMapperModule), |
||||
//typeof(AbpCachingStackExchangeRedisModule), |
||||
typeof(AbpEntityFrameworkCorePostgreSqlModule), |
||||
typeof(AbpAspNetCoreMvcUiBasicThemeModule), |
||||
typeof(AbpSwashbuckleModule), |
||||
typeof(AbpAspNetCoreAuthenticationJwtBearerModule), |
||||
typeof(AbpAspNetCoreSerilogModule), |
||||
|
||||
// Account module packages |
||||
typeof(AbpAccountApplicationModule), |
||||
typeof(AbpAccountHttpApiModule), |
||||
typeof(AbpAccountWebIdentityServerModule), |
||||
|
||||
// Identity module packages |
||||
typeof(AbpPermissionManagementDomainIdentityModule), |
||||
typeof(AbpIdentityApplicationModule), |
||||
typeof(AbpIdentityHttpApiModule), |
||||
typeof(AbpIdentityEntityFrameworkCoreModule), |
||||
typeof(AbpIdentityServerEntityFrameworkCoreModule), |
||||
|
||||
// Audit logging module packages |
||||
typeof(AbpAuditLoggingEntityFrameworkCoreModule), |
||||
|
||||
// Permission Management module packages |
||||
typeof(AbpPermissionManagementApplicationModule), |
||||
typeof(AbpPermissionManagementHttpApiModule), |
||||
typeof(AbpPermissionManagementEntityFrameworkCoreModule), |
||||
|
||||
// Tenant Management module packages |
||||
typeof(AbpTenantManagementApplicationModule), |
||||
typeof(AbpTenantManagementHttpApiModule), |
||||
typeof(AbpTenantManagementEntityFrameworkCoreModule), |
||||
|
||||
// Feature Management module packages |
||||
typeof(AbpFeatureManagementApplicationModule), |
||||
typeof(AbpFeatureManagementEntityFrameworkCoreModule), |
||||
typeof(AbpFeatureManagementHttpApiModule), |
||||
|
||||
// Setting Management module packages |
||||
typeof(AbpSettingManagementApplicationModule), |
||||
typeof(AbpSettingManagementEntityFrameworkCoreModule), |
||||
typeof(AbpSettingManagementHttpApiModule), |
||||
|
||||
typeof(AbpAspNetCoreMvcWrapperModule) |
||||
)] |
||||
public class BookStoreModule : AbpModule |
||||
{ |
||||
/* Single point to enable/disable multi-tenancy */ |
||||
private const bool IsMultiTenant = true; |
||||
|
||||
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
{ |
||||
context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => |
||||
{ |
||||
options.AddAssemblyResource( |
||||
typeof(BookStoreResource) |
||||
); |
||||
}); |
||||
} |
||||
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
{ |
||||
var hostingEnvironment = context.Services.GetHostingEnvironment(); |
||||
var configuration = context.Services.GetConfiguration(); |
||||
|
||||
context.Services.AddAlwaysAllowAuthorization(); |
||||
// wrap |
||||
Configure<AbpWrapperOptions>(options => |
||||
{ |
||||
options.IsEnabled = true; |
||||
options.IgnoreNamespaces.Clear(); |
||||
}); |
||||
|
||||
ConfigureBundles(); |
||||
ConfigureMultiTenancy(); |
||||
ConfigureUrls(configuration); |
||||
ConfigureAutoMapper(); |
||||
ConfigureSwagger(context.Services); |
||||
ConfigureAutoApiControllers(); |
||||
ConfigureVirtualFiles(hostingEnvironment); |
||||
ConfigureLocalization(); |
||||
ConfigureAuthentication(context.Services, configuration); |
||||
ConfigureCors(context, configuration); |
||||
//ConfigureDataProtection(context, configuration, hostingEnvironment); |
||||
ConfigureEfCore(context); |
||||
} |
||||
|
||||
private void ConfigureBundles() |
||||
{ |
||||
Configure<AbpBundlingOptions>(options => |
||||
{ |
||||
options.StyleBundles.Configure( |
||||
BasicThemeBundles.Styles.Global, |
||||
bundle => { bundle.AddFiles("/global-styles.css"); } |
||||
); |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureMultiTenancy() |
||||
{ |
||||
Configure<AbpMultiTenancyOptions>(options => |
||||
{ |
||||
options.IsEnabled = IsMultiTenant; |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureUrls(IConfiguration configuration) |
||||
{ |
||||
Configure<AppUrlOptions>(options => |
||||
{ |
||||
options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; |
||||
options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); |
||||
|
||||
options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"]; |
||||
options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password"; |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureAuthentication(IServiceCollection services, IConfiguration configuration) |
||||
{ |
||||
services.AddAuthentication() |
||||
.AddJwtBearer(options => |
||||
{ |
||||
options.Authority = configuration["AuthServer:Authority"]; |
||||
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]); |
||||
options.Audience = "BookStore"; |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureLocalization() |
||||
{ |
||||
Configure<AbpLocalizationOptions>(options => |
||||
{ |
||||
options.Resources |
||||
.Add<BookStoreResource>("en") |
||||
.AddBaseTypes(typeof(AbpValidationResource)) |
||||
.AddVirtualJson("/Localization/BookStore"); |
||||
|
||||
options.DefaultResourceType = typeof(BookStoreResource); |
||||
|
||||
options.Languages.Add(new LanguageInfo("en", "en", "English")); |
||||
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); |
||||
}); |
||||
|
||||
Configure<AbpExceptionLocalizationOptions>(options => |
||||
{ |
||||
options.MapCodeNamespace("BookStore", typeof(BookStoreResource)); |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureVirtualFiles(IWebHostEnvironment hostingEnvironment) |
||||
{ |
||||
Configure<AbpVirtualFileSystemOptions>(options => |
||||
{ |
||||
options.FileSets.AddEmbedded<BookStoreModule>(); |
||||
if (hostingEnvironment.IsDevelopment()) |
||||
{ |
||||
/* Using physical files in development, so we don't need to recompile on changes */ |
||||
options.FileSets.ReplaceEmbeddedByPhysical<BookStoreModule>(hostingEnvironment.ContentRootPath); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureAutoApiControllers() |
||||
{ |
||||
Configure<AbpAspNetCoreMvcOptions>(options => |
||||
{ |
||||
options.ConventionalControllers.Create(typeof(BookStoreModule).Assembly); |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureSwagger(IServiceCollection services) |
||||
{ |
||||
services.AddAbpSwaggerGen( |
||||
options => |
||||
{ |
||||
options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookStore API", Version = "v1" }); |
||||
options.DocInclusionPredicate((docName, description) => true); |
||||
options.CustomSchemaIds(type => type.FullName); |
||||
} |
||||
); |
||||
} |
||||
|
||||
private void ConfigureAutoMapper() |
||||
{ |
||||
Configure<AbpAutoMapperOptions>(options => |
||||
{ |
||||
/* Uncomment `validate: true` if you want to enable the Configuration Validation feature. |
||||
* See AutoMapper's documentation to learn what it is: |
||||
* https://docs.automapper.org/en/stable/Configuration-validation.html |
||||
*/ |
||||
options.AddMaps<BookStoreModule>(/* validate: true */); |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration) |
||||
{ |
||||
context.Services.AddCors(options => |
||||
{ |
||||
options.AddDefaultPolicy(builder => |
||||
{ |
||||
builder |
||||
.WithOrigins( |
||||
configuration["App:CorsOrigins"] |
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries) |
||||
.Select(o => o.RemovePostFix("/")) |
||||
.ToArray() |
||||
) |
||||
.WithAbpExposedHeaders() |
||||
.SetIsOriginAllowedToAllowWildcardSubdomains() |
||||
.AllowAnyHeader() |
||||
.AllowAnyMethod() |
||||
.AllowCredentials(); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
private void ConfigureDataProtection( |
||||
ServiceConfigurationContext context, |
||||
IConfiguration configuration, |
||||
IWebHostEnvironment hostingEnvironment) |
||||
{ |
||||
var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("BookStore"); |
||||
if (!hostingEnvironment.IsDevelopment()) |
||||
{ |
||||
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); |
||||
dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "BookStore-Protection-Keys"); |
||||
} |
||||
} |
||||
|
||||
private void ConfigureEfCore(ServiceConfigurationContext context) |
||||
{ |
||||
context.Services.AddAbpDbContext<BookStoreDbContext>(options => |
||||
{ |
||||
options.AddDefaultRepositories(includeAllEntities: true); |
||||
}); |
||||
|
||||
Configure<AbpDbContextOptions>(options => |
||||
{ |
||||
options.Configure(configurationContext => |
||||
{ |
||||
configurationContext.UseNpgsql(); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
||||
{ |
||||
var app = context.GetApplicationBuilder(); |
||||
var env = context.GetEnvironment(); |
||||
|
||||
if (env.IsDevelopment()) |
||||
{ |
||||
app.UseDeveloperExceptionPage(); |
||||
} |
||||
|
||||
app.UseAbpRequestLocalization(); |
||||
|
||||
if (!env.IsDevelopment()) |
||||
{ |
||||
app.UseErrorPage(); |
||||
} |
||||
|
||||
app.UseCorrelationId(); |
||||
app.UseStaticFiles(); |
||||
app.UseRouting(); |
||||
app.UseCors(); |
||||
app.UseAuthentication(); |
||||
app.UseJwtTokenMiddleware(); |
||||
|
||||
if (IsMultiTenant) |
||||
{ |
||||
app.UseMultiTenancy(); |
||||
} |
||||
|
||||
app.UseUnitOfWork(); |
||||
app.UseIdentityServer(); |
||||
app.UseAuthorization(); |
||||
|
||||
app.UseSwagger(); |
||||
app.UseAbpSwaggerUI(options => |
||||
{ |
||||
options.SwaggerEndpoint("/swagger/v1/swagger.json", "BookStore API"); |
||||
}); |
||||
|
||||
app.UseAuditing(); |
||||
app.UseAbpSerilogEnrichers(); |
||||
app.UseConfiguredEndpoints(); |
||||
} |
||||
} |
@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
|
||||
namespace BookStore.Controllers; |
||||
|
||||
public class HomeController : AbpController |
||||
{ |
||||
public ActionResult Index() |
||||
{ |
||||
return Redirect("~/swagger"); |
||||
} |
||||
} |
@ -0,0 +1,46 @@
|
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Volo.Abp; |
||||
using Volo.Abp.AspNetCore.Mvc; |
||||
using Volo.Abp.Authorization; |
||||
|
||||
namespace BookStore.Controllers; |
||||
|
||||
[Route("api/wrap")] |
||||
public class WrapController : AbpControllerBase |
||||
{ |
||||
[HttpGet("string")] |
||||
public Task<string> GetResultAsync() |
||||
{ |
||||
return Task.FromResult("Hello!"); |
||||
} |
||||
|
||||
[HttpGet("person")] |
||||
public Task<Person> GetPersonAsync() |
||||
{ |
||||
var person = new Person |
||||
{ |
||||
Name = "wwwk", |
||||
Age = 18 |
||||
}; |
||||
|
||||
return Task.FromResult(person); |
||||
} |
||||
|
||||
[HttpGet("throw-exception")] |
||||
public Task ThrowException() |
||||
{ |
||||
throw new UserFriendlyException("触发了一个异常!"); |
||||
} |
||||
|
||||
[HttpGet("throw-auth-exception")] |
||||
public Task ThrowAuthException() |
||||
{ |
||||
throw new AbpAuthorizationException(code: AbpAuthorizationErrorCodes.GivenPolicyHasNotGrantedForGivenResource); |
||||
} |
||||
} |
||||
|
||||
public class Person |
||||
{ |
||||
public string? Name { get; set; } |
||||
public int Age { get; set; } |
||||
} |
@ -0,0 +1,36 @@
|
||||
using Microsoft.EntityFrameworkCore; |
||||
using Volo.Abp.AuditLogging.EntityFrameworkCore; |
||||
using Volo.Abp.EntityFrameworkCore; |
||||
using Volo.Abp.FeatureManagement.EntityFrameworkCore; |
||||
using Volo.Abp.Identity.EntityFrameworkCore; |
||||
using Volo.Abp.IdentityServer.EntityFrameworkCore; |
||||
using Volo.Abp.PermissionManagement.EntityFrameworkCore; |
||||
using Volo.Abp.SettingManagement.EntityFrameworkCore; |
||||
using Volo.Abp.TenantManagement.EntityFrameworkCore; |
||||
|
||||
namespace BookStore.Data; |
||||
|
||||
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext> |
||||
{ |
||||
public BookStoreDbContext(DbContextOptions<BookStoreDbContext> options) |
||||
: base(options) |
||||
{ |
||||
} |
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder) |
||||
{ |
||||
base.OnModelCreating(builder); |
||||
|
||||
/* Include modules to your migration db context */ |
||||
|
||||
builder.ConfigurePermissionManagement(); |
||||
builder.ConfigureSettingManagement(); |
||||
builder.ConfigureAuditLogging(); |
||||
builder.ConfigureIdentity(); |
||||
builder.ConfigureIdentityServer(); |
||||
builder.ConfigureFeatureManagement(); |
||||
builder.ConfigureTenantManagement(); |
||||
|
||||
/* Configure your own entities here */ |
||||
} |
||||
} |
@ -0,0 +1,203 @@
|
||||
using System.Diagnostics; |
||||
using System.Runtime.InteropServices; |
||||
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 BookStore.Data; |
||||
|
||||
public class BookStoreDbMigrationService : ITransientDependency |
||||
{ |
||||
public ILogger<BookStoreDbMigrationService> Logger { get; set; } |
||||
|
||||
private readonly IDataSeeder _dataSeeder; |
||||
private readonly BookStoreEFCoreDbSchemaMigrator _dbSchemaMigrator; |
||||
private readonly ITenantRepository _tenantRepository; |
||||
private readonly ICurrentTenant _currentTenant; |
||||
|
||||
public BookStoreDbMigrationService( |
||||
IDataSeeder dataSeeder, |
||||
BookStoreEFCoreDbSchemaMigrator dbSchemaMigrator, |
||||
ITenantRepository tenantRepository, |
||||
ICurrentTenant currentTenant) |
||||
{ |
||||
_dataSeeder = dataSeeder; |
||||
_dbSchemaMigrator = dbSchemaMigrator; |
||||
_tenantRepository = tenantRepository; |
||||
_currentTenant = currentTenant; |
||||
|
||||
Logger = NullLogger<BookStoreDbMigrationService>.Instance; |
||||
} |
||||
|
||||
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..."); |
||||
await _dbSchemaMigrator.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; |
||||
} |
||||
else |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
Logger.LogWarning("Couldn't determinate if any migrations exist : " + e.Message); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
private bool DbMigrationsProjectExists() |
||||
{ |
||||
return Directory.Exists(GetEntityFrameworkCoreProjectFolderPath()); |
||||
} |
||||
|
||||
private bool MigrationsFolderExists() |
||||
{ |
||||
var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath(); |
||||
|
||||
return 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()}\" --nolayers\"" |
||||
); |
||||
|
||||
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!"); |
||||
} |
||||
|
||||
return Path.Combine(slnDirectoryPath, "BookStore"); |
||||
} |
||||
|
||||
private string GetSolutionDirectoryPath() |
||||
{ |
||||
var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()); |
||||
|
||||
while (Directory.GetParent(currentDirectory.FullName) != null) |
||||
{ |
||||
currentDirectory = Directory.GetParent(currentDirectory.FullName); |
||||
|
||||
if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) |
||||
{ |
||||
return currentDirectory.FullName; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore; |
||||
using Volo.Abp.DependencyInjection; |
||||
|
||||
namespace BookStore.Data; |
||||
|
||||
public class BookStoreEFCoreDbSchemaMigrator : ITransientDependency |
||||
{ |
||||
private readonly IServiceProvider _serviceProvider; |
||||
|
||||
public BookStoreEFCoreDbSchemaMigrator( |
||||
IServiceProvider serviceProvider) |
||||
{ |
||||
_serviceProvider = serviceProvider; |
||||
} |
||||
|
||||
public async Task MigrateAsync() |
||||
{ |
||||
/* We intentionally resolving the BookStoreDbContext |
||||
* from IServiceProvider (instead of directly injecting it) |
||||
* to properly get the connection string of the current tenant in the |
||||
* current scope. |
||||
*/ |
||||
|
||||
await _serviceProvider |
||||
.GetRequiredService<BookStoreDbContext>() |
||||
.Database |
||||
.MigrateAsync(); |
||||
} |
||||
} |
@ -0,0 +1,271 @@
|
||||
using IdentityServer4.Models; |
||||
using Volo.Abp.Authorization.Permissions; |
||||
using Volo.Abp.Data; |
||||
using Volo.Abp.DependencyInjection; |
||||
using Volo.Abp.Guids; |
||||
using Volo.Abp.IdentityServer.ApiResources; |
||||
using Volo.Abp.IdentityServer.ApiScopes; |
||||
using Volo.Abp.IdentityServer.Clients; |
||||
using Volo.Abp.IdentityServer.IdentityResources; |
||||
using Volo.Abp.MultiTenancy; |
||||
using Volo.Abp.PermissionManagement; |
||||
using Volo.Abp.Uow; |
||||
using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource; |
||||
using ApiScope = Volo.Abp.IdentityServer.ApiScopes.ApiScope; |
||||
using Client = Volo.Abp.IdentityServer.Clients.Client; |
||||
|
||||
namespace BookStore.Data; |
||||
|
||||
public class IdentityServerDataSeedContributor : IDataSeedContributor, ITransientDependency |
||||
{ |
||||
private readonly IApiResourceRepository _apiResourceRepository; |
||||
private readonly IApiScopeRepository _apiScopeRepository; |
||||
private readonly IClientRepository _clientRepository; |
||||
private readonly IIdentityResourceDataSeeder _identityResourceDataSeeder; |
||||
private readonly IGuidGenerator _guidGenerator; |
||||
private readonly IPermissionDataSeeder _permissionDataSeeder; |
||||
private readonly IConfiguration _configuration; |
||||
private readonly ICurrentTenant _currentTenant; |
||||
|
||||
public IdentityServerDataSeedContributor( |
||||
IClientRepository clientRepository, |
||||
IApiResourceRepository apiResourceRepository, |
||||
IApiScopeRepository apiScopeRepository, |
||||
IIdentityResourceDataSeeder identityResourceDataSeeder, |
||||
IGuidGenerator guidGenerator, |
||||
IPermissionDataSeeder permissionDataSeeder, |
||||
IConfiguration configuration, |
||||
ICurrentTenant currentTenant) |
||||
{ |
||||
_clientRepository = clientRepository; |
||||
_apiResourceRepository = apiResourceRepository; |
||||
_apiScopeRepository = apiScopeRepository; |
||||
_identityResourceDataSeeder = identityResourceDataSeeder; |
||||
_guidGenerator = guidGenerator; |
||||
_permissionDataSeeder = permissionDataSeeder; |
||||
_configuration = configuration; |
||||
_currentTenant = currentTenant; |
||||
} |
||||
|
||||
[UnitOfWork] |
||||
public virtual async Task SeedAsync(DataSeedContext context) |
||||
{ |
||||
using (_currentTenant.Change(context?.TenantId)) |
||||
{ |
||||
await _identityResourceDataSeeder.CreateStandardResourcesAsync(); |
||||
await CreateApiResourcesAsync(); |
||||
await CreateApiScopesAsync(); |
||||
await CreateClientsAsync(); |
||||
} |
||||
} |
||||
|
||||
private async Task CreateApiScopesAsync() |
||||
{ |
||||
await CreateApiScopeAsync("BookStore"); |
||||
} |
||||
|
||||
private async Task CreateApiResourcesAsync() |
||||
{ |
||||
var commonApiUserClaims = new[] {"email", "email_verified", "name", "phone_number", "phone_number_verified", "role"}; |
||||
await CreateApiResourceAsync("BookStore", commonApiUserClaims); |
||||
} |
||||
|
||||
private async Task<ApiResource> CreateApiResourceAsync(string name, IEnumerable<string> claims) |
||||
{ |
||||
var apiResource = await _apiResourceRepository.FindByNameAsync(name); |
||||
if (apiResource == null) |
||||
{ |
||||
apiResource = await _apiResourceRepository.InsertAsync( |
||||
new ApiResource( |
||||
_guidGenerator.Create(), |
||||
name, |
||||
name + " API" |
||||
), |
||||
autoSave: true |
||||
); |
||||
} |
||||
|
||||
foreach (var claim in claims) |
||||
{ |
||||
if (apiResource.FindClaim(claim) == null) |
||||
{ |
||||
apiResource.AddUserClaim(claim); |
||||
} |
||||
} |
||||
|
||||
return await _apiResourceRepository.UpdateAsync(apiResource); |
||||
} |
||||
|
||||
private async Task<ApiScope> CreateApiScopeAsync(string name) |
||||
{ |
||||
var apiScope = await _apiScopeRepository.FindByNameAsync(name); |
||||
if (apiScope == null) |
||||
{ |
||||
apiScope = await _apiScopeRepository.InsertAsync( |
||||
new ApiScope( |
||||
_guidGenerator.Create(), |
||||
name, |
||||
name + " API" |
||||
), |
||||
autoSave: true |
||||
); |
||||
} |
||||
|
||||
return apiScope; |
||||
} |
||||
|
||||
private async Task CreateClientsAsync() |
||||
{ |
||||
var commonScopes = new[] |
||||
{ |
||||
"email", |
||||
"openid", |
||||
"profile", |
||||
"role", |
||||
"phone", |
||||
"address", |
||||
"BookStore" |
||||
}; |
||||
|
||||
var configurationSection = _configuration.GetSection("IdentityServer:Clients"); |
||||
|
||||
// Angular Client |
||||
var consoleAndAngularClientId = configurationSection["BookStore_App:ClientId"]; |
||||
if (!consoleAndAngularClientId.IsNullOrWhiteSpace()) |
||||
{ |
||||
var webClientRootUrl = configurationSection["BookStore_App:RootUrl"]?.TrimEnd('/'); |
||||
|
||||
await CreateClientAsync( |
||||
name: consoleAndAngularClientId, |
||||
scopes: commonScopes, |
||||
grantTypes: new[] { "password", "client_credentials", "authorization_code" }, |
||||
secret: (configurationSection["BookStore_App:ClientSecret"] ?? "1q2w3e*").Sha256(), |
||||
requireClientSecret: false, |
||||
redirectUri: webClientRootUrl, |
||||
postLogoutRedirectUri: webClientRootUrl, |
||||
corsOrigins: new[] { webClientRootUrl.RemovePostFix("/") } |
||||
); |
||||
} |
||||
|
||||
// Swagger Client |
||||
var swaggerClientId = configurationSection["BookStore_Swagger:ClientId"]; |
||||
if (!swaggerClientId.IsNullOrWhiteSpace()) |
||||
{ |
||||
var swaggerRootUrl = configurationSection["BookStore_Swagger:RootUrl"].TrimEnd('/'); |
||||
|
||||
await CreateClientAsync( |
||||
name: swaggerClientId, |
||||
scopes: commonScopes, |
||||
grantTypes: new[] { "authorization_code" }, |
||||
secret: configurationSection["BookStore_Swagger:ClientSecret"]?.Sha256(), |
||||
requireClientSecret: false, |
||||
redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html", |
||||
corsOrigins: new[] { swaggerRootUrl.RemovePostFix("/") } |
||||
); |
||||
} |
||||
} |
||||
|
||||
private async Task<Client> CreateClientAsync( |
||||
string name, |
||||
IEnumerable<string> scopes, |
||||
IEnumerable<string> grantTypes, |
||||
string secret = null, |
||||
string redirectUri = null, |
||||
string postLogoutRedirectUri = null, |
||||
string frontChannelLogoutUri = null, |
||||
bool requireClientSecret = true, |
||||
bool requirePkce = false, |
||||
IEnumerable<string> permissions = null, |
||||
IEnumerable<string> corsOrigins = null) |
||||
{ |
||||
var client = await _clientRepository.FindByClientIdAsync(name); |
||||
if (client == null) |
||||
{ |
||||
client = await _clientRepository.InsertAsync( |
||||
new Client( |
||||
_guidGenerator.Create(), |
||||
name |
||||
) |
||||
{ |
||||
ClientName = name, |
||||
ProtocolType = "oidc", |
||||
Description = name, |
||||
AlwaysIncludeUserClaimsInIdToken = true, |
||||
AllowOfflineAccess = true, |
||||
AbsoluteRefreshTokenLifetime = 31536000, //365 days |
||||
AccessTokenLifetime = 31536000, //365 days |
||||
AuthorizationCodeLifetime = 300, |
||||
IdentityTokenLifetime = 300, |
||||
RequireConsent = false, |
||||
FrontChannelLogoutUri = frontChannelLogoutUri, |
||||
RequireClientSecret = requireClientSecret, |
||||
RequirePkce = requirePkce |
||||
}, |
||||
autoSave: true |
||||
); |
||||
} |
||||
|
||||
foreach (var scope in scopes) |
||||
{ |
||||
if (client.FindScope(scope) == null) |
||||
{ |
||||
client.AddScope(scope); |
||||
} |
||||
} |
||||
|
||||
foreach (var grantType in grantTypes) |
||||
{ |
||||
if (client.FindGrantType(grantType) == null) |
||||
{ |
||||
client.AddGrantType(grantType); |
||||
} |
||||
} |
||||
|
||||
if (!secret.IsNullOrEmpty()) |
||||
{ |
||||
if (client.FindSecret(secret) == null) |
||||
{ |
||||
client.AddSecret(secret); |
||||
} |
||||
} |
||||
|
||||
if (redirectUri != null) |
||||
{ |
||||
if (client.FindRedirectUri(redirectUri) == null) |
||||
{ |
||||
client.AddRedirectUri(redirectUri); |
||||
} |
||||
} |
||||
|
||||
if (postLogoutRedirectUri != null) |
||||
{ |
||||
if (client.FindPostLogoutRedirectUri(postLogoutRedirectUri) == null) |
||||
{ |
||||
client.AddPostLogoutRedirectUri(postLogoutRedirectUri); |
||||
} |
||||
} |
||||
|
||||
if (permissions != null) |
||||
{ |
||||
await _permissionDataSeeder.SeedAsync( |
||||
ClientPermissionValueProvider.ProviderName, |
||||
name, |
||||
permissions, |
||||
null |
||||
); |
||||
} |
||||
|
||||
if (corsOrigins != null) |
||||
{ |
||||
foreach (var origin in corsOrigins) |
||||
{ |
||||
if (!origin.IsNullOrWhiteSpace() && client.FindCorsOrigin(origin) == null) |
||||
{ |
||||
client.AddCorsOrigin(origin); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return await _clientRepository.UpdateAsync(client); |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "ar", |
||||
"texts": { |
||||
"Welcome_Title": "مرحبا", |
||||
"Welcome_Text": "هذا هو قالب بدء تشغيل تطبيق ذو طبقة واحدة مبسط لإطار عمل ABP.", |
||||
"Menu:Home": "الرئيسية" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "cs", |
||||
"texts": { |
||||
"Welcome_Title": "Vítejte", |
||||
"Welcome_Text": "Toto je minimalistická šablona pro spuštění aplikace s jednou vrstvou pro ABP Framework.", |
||||
"Menu:Home": "Úvod" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "de-DE", |
||||
"texts": { |
||||
"Welcome_Title": "Willkommen", |
||||
"Welcome_Text": "Dies ist eine minimalistische, einschichtige Anwendungsstartvorlage für das ABP-Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "en-GB", |
||||
"texts": { |
||||
"Welcome_Title": "Welcome_Title", |
||||
"Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "en", |
||||
"texts": { |
||||
"Welcome_Title": "Welcome", |
||||
"Welcome_Text": "This is a minimalist, single layer application startup template for the ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "es", |
||||
"texts": { |
||||
"Welcome_Title": "Bienvenido", |
||||
"Welcome_Text": "Esta es una plantilla de inicio de aplicación minimalista de una sola capa para ABP Framework.", |
||||
"Menu:Home": "Inicio" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "fi", |
||||
"texts": { |
||||
"Welcome_Title": "Tervetuloa", |
||||
"Welcome_Text": "Tämä on minimalistinen yksikerroksinen sovelluksen käynnistysmalli ABP Frameworkille.", |
||||
"Menu:Home": "Koti" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "fr", |
||||
"texts": { |
||||
"Welcome_Title": "Bienvenue", |
||||
"Welcome_Text": "Il s'agit d'un modèle de démarrage d'application minimaliste à une seule couche pour le cadre ABP.", |
||||
"Menu:Home": "Accueil" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "hu", |
||||
"texts": { |
||||
"Welcome_Title": "Üdvözlöm", |
||||
"Welcome_Text": "Ez egy minimalista, egyrétegű alkalmazásindítási sablon az ABP-keretrendszerhez.", |
||||
"Menu:Home": "Kezdőlap" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "is", |
||||
"texts": { |
||||
"Welcome_Title": "Velkomin", |
||||
"Welcome_Text": "Þetta er lægstur, eins lags ræsingarsniðmát fyrir ABP Framework.", |
||||
"Menu:Home": "Heim" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "it", |
||||
"texts": { |
||||
"Welcome_Title": "Benvenuto", |
||||
"Welcome_Text": "Questo è un modello di avvio dell'applicazione minimalista a livello singolo per ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "nl", |
||||
"texts": { |
||||
"Welcome_Title": "Welkom", |
||||
"Welcome_Text": "Dit is een minimalistische, enkellaagse applicatie-opstartsjabloon voor het ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "pl-PL", |
||||
"texts": { |
||||
"Welcome_Title": "Witaj", |
||||
"Welcome_Text": "Jest to minimalistyczny, jednowarstwowy szablon uruchamiania aplikacji dla ABP Framework.", |
||||
"Menu:Home": "Home" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "pt-BR", |
||||
"texts": { |
||||
"Welcome_Title": "Seja bem-vindo!", |
||||
"Welcome_Text": "Este é um modelo de inicialização de aplicativo de camada única minimalista para o ABP Framework.", |
||||
"Menu:Home": "Principal" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "ro-RO", |
||||
"texts": { |
||||
"Welcome_Title": "Bun venit", |
||||
"Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", |
||||
"Menu:Home": "Acasă" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "ru", |
||||
"texts": { |
||||
"Welcome_Title": "Bine ati venit", |
||||
"Welcome_Text": "Acesta este un șablon de pornire a aplicației minimaliste, cu un singur strat, pentru Cadrul ABP.", |
||||
"Menu:Home": "Главная" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "sk", |
||||
"texts": { |
||||
"Welcome_Title": "Vitajte", |
||||
"Welcome_Text": "Toto je minimalistická šablóna na spustenie aplikácie s jednou vrstvou pre rámec ABP.", |
||||
"Menu:Home": "Domov" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "sl", |
||||
"texts": { |
||||
"Welcome_Title": "Dobrodošli", |
||||
"Welcome_Text": "To je minimalistična enoslojna predloga za zagon aplikacije za ABP Framework.", |
||||
"Menu:Home": "Domov" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "tr", |
||||
"texts": { |
||||
"Welcome_Title": "Hoşgeldiniz", |
||||
"Welcome_Text": "Bu proje tek katmanlı ABP uygulamaları yapmak için bir başlangıç şablonudur.", |
||||
"Menu:Home": "Ana sayfa" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "vi", |
||||
"texts": { |
||||
"Welcome_Title": "Chào mừng bạn", |
||||
"Welcome_Text": "Đây là một mẫu khởi động ứng dụng lớp đơn, tối giản cho ABP Framework.", |
||||
"Menu:Home": "Trang chủ" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "zh-Hans", |
||||
"texts": { |
||||
"Welcome_Title": "欢迎", |
||||
"Welcome_Text": "这是ABP框架的极简单层应用程序启动模板.", |
||||
"Menu:Home": "首页" |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"culture": "zh-Hant", |
||||
"texts": { |
||||
"Welcome_Title": "歡迎", |
||||
"Welcome_Text": "這是 ABP 框架的極簡單層應用程序啟動模板.", |
||||
"Menu:Home": "首頁" |
||||
} |
||||
} |
@ -0,0 +1,9 @@
|
||||
using Volo.Abp.Localization; |
||||
|
||||
namespace BookStore.Localization; |
||||
|
||||
[LocalizationResourceName("BookStore")] |
||||
public class BookStoreResource |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,11 @@
|
||||
using AutoMapper; |
||||
|
||||
namespace BookStore.ObjectMapping; |
||||
|
||||
public class BookStoreAutoMapperProfile : Profile |
||||
{ |
||||
public BookStoreAutoMapperProfile() |
||||
{ |
||||
/* Create your AutoMapper object mappings here */ |
||||
} |
||||
} |
@ -0,0 +1,77 @@
|
||||
using System; |
||||
using BookStore.Data; |
||||
using Serilog; |
||||
using Serilog.Events; |
||||
|
||||
namespace BookStore; |
||||
|
||||
public class Program |
||||
{ |
||||
public async static Task<int> Main(string[] args) |
||||
{ |
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); |
||||
var loggerConfiguration = new LoggerConfiguration() |
||||
#if DEBUG |
||||
.MinimumLevel.Debug() |
||||
#else |
||||
.MinimumLevel.Information() |
||||
#endif |
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information) |
||||
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) |
||||
.Enrich.FromLogContext() |
||||
#if DEBUG |
||||
.WriteTo.Async(c => c.File("Logs/logs.txt")) |
||||
.WriteTo.Async(c => c.Console()); |
||||
#else |
||||
.WriteTo.Async(c => c.File("Logs/logs.txt")); |
||||
#endif |
||||
if (IsMigrateDatabase(args)) |
||||
{ |
||||
loggerConfiguration.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning); |
||||
loggerConfiguration.MinimumLevel.Override("Microsoft", LogEventLevel.Warning); |
||||
loggerConfiguration.MinimumLevel.Override("IdentityServer4.Startup", LogEventLevel.Warning); |
||||
} |
||||
|
||||
Log.Logger = loggerConfiguration.CreateLogger(); |
||||
|
||||
try |
||||
{ |
||||
var builder = WebApplication.CreateBuilder(args); |
||||
builder.Host.AddAppSettingsSecretsJson() |
||||
.UseAutofac() |
||||
.UseSerilog(); |
||||
await builder.AddApplicationAsync<BookStoreModule>(); |
||||
var app = builder.Build(); |
||||
await app.InitializeApplicationAsync(); |
||||
|
||||
if (IsMigrateDatabase(args)) |
||||
{ |
||||
await app.Services.GetRequiredService<BookStoreDbMigrationService>().MigrateAsync(); |
||||
return 0; |
||||
} |
||||
|
||||
Log.Information("Starting BookStore."); |
||||
await app.RunAsync(); |
||||
return 0; |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
if (ex.GetType().Name.Equals("StopTheHostException", StringComparison.Ordinal)) |
||||
{ |
||||
throw; |
||||
} |
||||
|
||||
Log.Fatal(ex, "BookStore terminated unexpectedly!"); |
||||
return 1; |
||||
} |
||||
finally |
||||
{ |
||||
Log.CloseAndFlush(); |
||||
} |
||||
} |
||||
|
||||
private static bool IsMigrateDatabase(string[] args) |
||||
{ |
||||
return args.Any(x => x.Contains("--migrate-database", StringComparison.OrdinalIgnoreCase)); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
{ |
||||
"iisSettings": { |
||||
"windowsAuthentication": false, |
||||
"anonymousAuthentication": true, |
||||
"iisExpress": { |
||||
"applicationUrl": "https://localhost:44357", |
||||
"sslPort": 44357 |
||||
} |
||||
}, |
||||
"profiles": { |
||||
"BookStore": { |
||||
"commandName": "Project", |
||||
"launchBrowser": true, |
||||
"applicationUrl": "https://localhost:44357", |
||||
"environmentVariables": { |
||||
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,37 @@
|
||||
{ |
||||
"App": { |
||||
"SelfUrl": "https://localhost:44357", |
||||
"ClientUrl": "http://localhost:4200", |
||||
"CorsOrigins": "https://*.BookStore.com,http://localhost:4200", |
||||
"RedirectAllowedUrls": "http://localhost:4200" |
||||
}, |
||||
"ConnectionStrings": { |
||||
"Default": "Host=localhost;Port=5432;Database=BookStore;User ID=postgres;Password=123456;" |
||||
}, |
||||
"Redis": { |
||||
"Configuration": "127.0.0.1" |
||||
}, |
||||
"AuthServer": { |
||||
"Authority": "https://localhost:44357", |
||||
"RequireHttpsMetadata": "false", |
||||
"SwaggerClientId": "BookStore_Swagger", |
||||
"SwaggerClientSecret": "1q2w3e*" |
||||
}, |
||||
"StringEncryption": { |
||||
"DefaultPassPhrase": "Q4ki8okY7ZyMNWDw" |
||||
}, |
||||
"IdentityServer": { |
||||
"Clients": { |
||||
"BookStore_App": { |
||||
"ClientId": "BookStore_App", |
||||
"ClientSecret": "1q2w3e*", |
||||
"RootUrl": "http://localhost:4200" |
||||
}, |
||||
"BookStore_Swagger": { |
||||
"ClientId": "BookStore_Swagger", |
||||
"ClientSecret": "1q2w3e*", |
||||
"RootUrl": "https://localhost:44357" |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,8 @@
|
||||
{ |
||||
"version": "1.0.0", |
||||
"name": "my-app", |
||||
"private": true, |
||||
"dependencies": { |
||||
"@abp/aspnetcore.mvc.ui.theme.basic": "^5.2.0-rc.2" |
||||
} |
||||
} |
@ -0,0 +1 @@
|
||||
{"AdditionalData":{},"Alg":"RS256","Crv":null,"D":"PqIhXe397CUD48Ulbw-kT8MwuWFckXy3hGj-huiX63FpBSRCRd9cmV81h5WI0RX6i0nrKZ2go5cakNgIqx_8gFoOP5N5j8umcU32UtwXePilRjP9RrY_9ub-P0iSmqIEQEZSOg93W7sOVL9jnvd0BLBavqOmlErz9JhU3QU9ebY3lDSdTHZWqYSVb6yeXjZGCRYZgkCgVYx1CiKkFRA5MEEM_WUBhLxVhNmbvFbwlp54PPaBBBEA3RYilv8aK0RBmhdgKRLpfMegEeM3yJh2D92EV2lx3DEhycia72AuqFQ-ZEZBAXVIjxcA8YjtsJXhaOqLnd30EbXHA-sP81kAcQ","DP":"OL_P-Fktuq1YC4YzLuhbRuO0QnkV2xYqPPYNTFJenhOafyPzT2yIAFlJooLcZX6Fe10yFsS918ozn50huIxdfgxrNw2I011-sXYGNuhWnej7OdIUnJkXdSo2Ub8mJpro3iN_524P5cxda9MD5bQAKN1SmgSDHsOZZJALVJBO4dE","DQ":"C2I3Kqu0zaLtYl4OuJxyiMtA2UmK88yFTnDm4pvSWj9XXiiBs2VYLwGevhDTcYI1gT8zar4xeUoWimHzSQxbOcp7EXsjqo_eMTt_t7gwjDRs9tSOu0_mTUhisuM996wAa-QVVzvonxtVPyzHSuoXoxNHfRAUDl1MPVcqV9imTJs","E":"AQAB","K":null,"KeyId":"E845B5C99290199A39E6D83B31E223FA","Kid":"E845B5C99290199A39E6D83B31E223FA","Kty":"RSA","N":"qjXtEBYJMcDnFV59Tk9wTd516-U6Pq_NDujOBZh3ccSvsKc0-NdkAST45WjBscXzh6ByAQcHss1oqrj6YttvzfcReA6IrPVSz1fvBi67eoCjuBVv_94aCoT1fmUpxgv2V9ZqEp23goJ2oppkyTcE7VIoKGgAQREzE4kJ3YssMPYNfR9g_14DbPTMkDYfxfGI7j4wjAOFFNPlWfBToyH7U7dgBtBeu-5NYxFrBZ_Ze0tOoJ20D_u9UZ93rAi7FVhY5787MltstmJ2R-ZNWgZjBad8s_xA6vXHP_7rBh-taAu4ID23U2DeL8w_4XncAPDNdQAZmDJ8_BcN_0uvkaVPNQ","Oth":null,"P":"05r8Rgv5TEAghb4HiCZeI6QcDi1VAXTzHI1f7r_0lliulzk_jc48i9iHGt2cJTaTba_Ln2LDAhkaIKMmptUXVvQiEzy02yeH93Iv9myQHORIwDkSvapMadOn2aCRCa_TJ3K_Bem8Ac9XKV8x4dG1nOtPOchCstfrJRM5g0HiqtM","Q":"zeuwW8cnB0JHvYpbn0dgZZf2tgwHcYNLCbHxsTGZsI6QZJxKC2EgmVFPwKiPLh2_qUegCkns4qUwt7_1eLtoqEplCmbR-rz_TO0zdKcO7-NA4jfud5nMgIIDMvECiaSSrpCoHy204823HXeEv3G6-jMyi7v2kBmfGZt_5RQByNc","QI":"aknrgXL1ZUNAbK4TcVPjG6j-9iCJTsV5LYtzud0DW1usLd67k_XSrCptK3ova__OfnnlzzJpqtCL1LFxk-WTn4ZVT8E1YcjYeaW9DDUQ80DtpYXaL0mv1P8b7ma7FE-m4nuknZeB6D5pYKXT7Tm2tvOHpYn38zzDpt7elFoohVU","Use":null,"X":null,"X5t":null,"X5tS256":null,"X5u":null,"Y":null,"KeySize":2048,"HasPrivateKey":true,"CryptoProviderFactory":{"CryptoProviderCache":{},"CustomCryptoProvider":null,"CacheSignatureProviders":true,"SignatureProviderObjectPoolCacheSize":32}} |
Loading…
Reference in new issue