Compare commits

..

8 Commits

Author SHA1 Message Date
8e58c83526 build: ignore files
All checks were successful
.NET Test Pipeline / build-and-test (push) Successful in 2m14s
2024-06-01 11:01:08 +03:00
2e64caf6ea refactor: code cleaning 2024-06-01 10:59:23 +03:00
9c56fa582b build: upgrade reference 2024-06-01 10:58:43 +03:00
b2a0a6dd7c feat: add default value attribute 2024-06-01 10:58:11 +03:00
78f589bb18 refactor: distribute configurations by classes 2024-06-01 10:57:52 +03:00
ca02509b97 build: add needed reference 2024-06-01 10:55:43 +03:00
f3a757d33d feat: add redis 2024-06-01 10:53:21 +03:00
34addd930f feat: add serilog for logging 2024-06-01 10:50:38 +03:00
17 changed files with 404 additions and 185 deletions

2
.gitignore vendored
View File

@ -361,3 +361,5 @@ MigrationBackup/
# Fody - auto-generated XML schema # Fody - auto-generated XML schema
FodyWeavers.xsd FodyWeavers.xsd
/ApiDto/ApiDtoDocs.xml
/Endpoint/docs.xml

View File

@ -50,8 +50,8 @@ public class GroupScheduleInfo
/// If is <see langword="null"/>, then there are no specific <see cref="Weeks"/> /// If is <see langword="null"/>, then there are no specific <see cref="Weeks"/>
/// </remarks> /// </remarks>
/// ///
public bool? IsExcludedWeeks { get; set; } public bool? IsExcludedWeeks { get; set; }
/// <summary> /// <summary>
/// The week numbers required for the correct display of the schedule. /// The week numbers required for the correct display of the schedule.
/// <br/> /// <br/>

View File

@ -1,6 +0,0 @@
@Backend_HostAddress = http://localhost:5269
GET {{Backend_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,9 @@
using System;
namespace Mirea.Api.Endpoint.Common.Attributes;
[AttributeUsage(AttributeTargets.Parameter)]
public class SwaggerDefaultAttribute(string value) : Attribute
{
public string Value { get; } = value;
}

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.DependencyInjection;
namespace Mirea.Api.Endpoint.Configuration.AppConfig;
public static class ApiVersioningConfiguration
{
public static void AddCustomApiVersioning(this IServiceCollection services)
{
services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
});
services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
services.AddEndpointsApiExplorer();
}
}

View File

@ -0,0 +1,23 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mirea.Api.Endpoint.Configuration.General.Settings;
namespace Mirea.Api.Endpoint.Configuration.AppConfig;
public static class CacheConfiguration
{
public static IServiceCollection AddCustomRedis(this IServiceCollection services, IConfiguration configuration)
{
var cache = configuration.Get<CacheSettings>();
if (cache?.TypeDatabase == CacheSettings.CacheEnum.Redis)
{
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = cache.ConnectionString;
options.InstanceName = "mirea_";
});
}
return services;
}
}

View File

@ -0,0 +1,68 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Mirea.Api.Endpoint.Configuration.AppConfig;
public static class EnvironmentConfiguration
{
private static IDictionary<string, string> LoadEnvironment(string envFile)
{
Dictionary<string, string> environment = new();
if (!File.Exists(envFile)) return environment;
foreach (var line in File.ReadAllLines(envFile))
{
if (string.IsNullOrEmpty(line)) continue;
var commentIndex = line.IndexOf('#', StringComparison.Ordinal);
string arg = line;
if (commentIndex != -1)
arg = arg.Remove(commentIndex, arg.Length - commentIndex);
var parts = arg.Split(
'=',
StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 2)
parts = [parts[0], string.Join("=", parts[1..])];
if (parts.Length != 2)
continue;
environment.Add(parts[0].Trim(), parts[1].Trim());
}
return environment;
}
public static IConfigurationRoot GetEnvironment()
{
var variablesFromFile = LoadEnvironment(".env");
var environmentVariables = Environment.GetEnvironmentVariables()
.OfType<DictionaryEntry>()
.ToDictionary(
entry => entry.Key.ToString() ?? string.Empty,
entry => entry.Value?.ToString() ?? string.Empty
);
var result = new ConfigurationBuilder()
.AddInMemoryCollection(environmentVariables!)
.AddInMemoryCollection(variablesFromFile!);
#if DEBUG
result.AddInMemoryCollection(LoadEnvironment(".env.develop")!);
#endif
Environment.SetEnvironmentVariable("PATH_TO_SAVE", variablesFromFile["PATH_TO_SAVE"]);
return result.Build();
}
}

View File

@ -0,0 +1,66 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Mirea.Api.Endpoint.Common.Services.Security;
using Mirea.Api.Security.Common.Interfaces;
using System;
using System.Text;
namespace Mirea.Api.Endpoint.Configuration.AppConfig;
public static class JwtConfiguration
{
public static IServiceCollection AddJwtToken(this IServiceCollection services, IConfiguration configuration)
{
var lifeTimeJwt = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_JWT"]!));
var jwtDecrypt = Encoding.UTF8.GetBytes(configuration["SECURITY_ENCRYPTION_TOKEN"] ?? string.Empty);
if (jwtDecrypt.Length != 32)
throw new InvalidOperationException("The secret token \"SECURITY_ENCRYPTION_TOKEN\" cannot be less than 32 characters long. Now the size is equal is " + jwtDecrypt.Length);
var jwtKey = Encoding.UTF8.GetBytes(configuration["SECURITY_SIGNING_TOKEN"] ?? string.Empty);
if (jwtKey.Length != 64)
throw new InvalidOperationException("The signature token \"SECURITY_SIGNING_TOKEN\" cannot be less than 64 characters. Now the size is " + jwtKey.Length);
var jwtIssuer = configuration["SECURITY_JWT_ISSUER"];
var jwtAudience = configuration["SECURITY_JWT_AUDIENCE"];
if (string.IsNullOrEmpty(jwtAudience) || string.IsNullOrEmpty(jwtIssuer))
throw new InvalidOperationException("The \"SECURITY_JWT_ISSUER\" and \"SECURITY_JWT_AUDIENCE\" are not specified");
services.AddSingleton<IAccessToken, JwtTokenService>(_ => new JwtTokenService
{
Audience = jwtAudience,
Issuer = jwtIssuer,
Lifetime = lifeTimeJwt,
EncryptionKey = jwtDecrypt,
SigningKey = jwtKey
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtIssuer,
ValidateAudience = true,
ValidAudience = jwtAudience,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(jwtKey),
TokenDecryptionKey = new SymmetricSecurityKey(jwtDecrypt)
};
});
return services;
}
}

View File

@ -0,0 +1,79 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Configuration.General;
using Serilog;
using Serilog.Events;
using Serilog.Filters;
using Serilog.Formatting.Compact;
using System.IO;
namespace Mirea.Api.Endpoint.Configuration.AppConfig;
public static class LoggerConfiguration
{
public static IHostBuilder AddCustomSerilog(this IHostBuilder hostBuilder)
{
hostBuilder.UseSerilog((context, _, configuration) =>
{
var generalConfig = context.Configuration.Get<GeneralConfig>();
configuration
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate:
"[{Level:u3}] [{Timestamp:dd.MM.yyyy HH:mm:ss}] {Message:lj}{NewLine}{Exception}");
if (generalConfig?.LogSettings?.EnableLogToFile == true)
{
if (!string.IsNullOrEmpty(generalConfig.LogSettings.LogFilePath) && Directory.Exists(PathBuilder.Combine(generalConfig.LogSettings.LogFilePath)))
Directory.CreateDirectory(generalConfig.LogSettings.LogFilePath);
configuration.WriteTo.File(
new CompactJsonFormatter(),
PathBuilder.Combine(
generalConfig.LogSettings.LogFilePath!,
generalConfig.LogSettings.LogFileName + ".json"
),
LogEventLevel.Debug,
rollingInterval: RollingInterval.Day);
}
configuration
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning);
configuration.Filter.ByExcluding(Matching.WithProperty<string>("SourceContext", sc =>
sc.Contains("Microsoft.EntityFrameworkCore.Database.Command")));
});
return hostBuilder;
}
public static IApplicationBuilder UseCustomSerilog(this IApplicationBuilder app)
{
app.UseSerilogRequestLogging(options =>
{
options.MessageTemplate = "Handled {RequestPath} in {Elapsed:0.0000} ms";
options.GetLevel = (_, elapsed, ex) => elapsed >= 2500 || ex != null
? LogEventLevel.Warning
: elapsed >= 1000
? LogEventLevel.Information
: LogEventLevel.Debug;
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
diagnosticContext.Set("UserAgent", httpContext.Request.Headers.UserAgent);
diagnosticContext.Set("RemoteIPAddress", httpContext.Connection.RemoteIpAddress?.ToString());
};
});
return app;
}
}

View File

@ -0,0 +1,26 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mirea.Api.Endpoint.Common.Services.Security;
using Mirea.Api.Endpoint.Configuration.General.Settings;
using Mirea.Api.Security;
using Mirea.Api.Security.Common.Interfaces;
namespace Mirea.Api.Endpoint.Configuration.AppConfig;
public static class SecureConfiguration
{
public static IServiceCollection AddSecurity(this IServiceCollection services, IConfiguration configuration)
{
services.AddSecurityServices(configuration);
services.AddSingleton<IAccessToken, JwtTokenService>();
services.AddSingleton<IRevokedToken, MemoryRevokedTokenService>();
if (configuration.Get<CacheSettings>()?.TypeDatabase == CacheSettings.CacheEnum.Redis)
services.AddSingleton<ICacheService, DistributedCacheService>();
else
services.AddSingleton<ICacheService, MemoryCacheService>();
return services;
}
}

View File

@ -0,0 +1,48 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Mirea.Api.Endpoint.Configuration.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.IO;
namespace Mirea.Api.Endpoint.Configuration.AppConfig;
public static class SwaggerConfiguration
{
public static IServiceCollection AddCustomSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(options =>
{
options.SchemaFilter<SwaggerExampleFilter>();
options.OperationFilter<SwaggerDefaultValues>();
var basePath = AppDomain.CurrentDomain.BaseDirectory;
options.IncludeXmlComments(Path.Combine(basePath, "docs.xml"));
options.IncludeXmlComments(Path.Combine(basePath, "ApiDtoDocs.xml"));
});
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
return services;
}
public static IApplicationBuilder UseCustomSwagger(this IApplicationBuilder app, IServiceProvider services)
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var provider = services.GetService<IApiVersionDescriptionProvider>();
foreach (var description in provider!.ApiVersionDescriptions)
{
var url = $"/swagger/{description.GroupName}/swagger.json";
var name = description.GroupName.ToUpperInvariant();
options.SwaggerEndpoint(url, name);
}
});
return app;
}
}

View File

@ -1,36 +0,0 @@
using System;
using System.IO;
namespace Mirea.Api.Endpoint.Configuration;
internal static class EnvironmentManager
{
public static void LoadEnvironment(string envFile)
{
if (!File.Exists(envFile)) return;
foreach (var line in File.ReadAllLines(envFile))
{
if (string.IsNullOrEmpty(line)) continue;
var commentIndex = line.IndexOf('#', StringComparison.Ordinal);
string arg = line;
if (commentIndex != -1)
arg = arg.Remove(commentIndex, arg.Length - commentIndex);
var parts = arg.Split(
'=',
StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 2)
parts = [parts[0], string.Join("=", parts[1..])];
if (parts.Length != 2)
continue;
Environment.SetEnvironmentVariable(parts[0].Trim(), parts[1].Trim());
}
}
}

View File

@ -1,7 +1,7 @@
using System; using Mirea.Api.DataAccess.Persistence.Common;
using Mirea.Api.DataAccess.Persistence.Common;
using Mirea.Api.Endpoint.Configuration.General.Attributes; using Mirea.Api.Endpoint.Configuration.General.Attributes;
using Mirea.Api.Endpoint.Configuration.General.Interfaces; using Mirea.Api.Endpoint.Configuration.General.Interfaces;
using System;
namespace Mirea.Api.Endpoint.Configuration.General.Settings; namespace Mirea.Api.Endpoint.Configuration.General.Settings;

View File

@ -0,0 +1,16 @@
using Microsoft.OpenApi.Models;
using Mirea.Api.Endpoint.Common.Attributes;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
namespace Mirea.Api.Endpoint.Configuration.Swagger;
public class SwaggerExampleFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var att = context.ParameterInfo?.GetCustomAttribute<SwaggerDefaultAttribute>();
if (att != null)
schema.Example = new Microsoft.OpenApi.Any.OpenApiString(att.Value);
}
}

View File

@ -24,7 +24,17 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Cronos" Version="0.8.4" /> <PackageReference Include="Cronos" Version="0.8.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
<PackageReference Include="StackExchange.Redis" Version="2.7.33" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.6" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.6.2" />
<PackageReference Include="Swashbuckle.AspNetCore.Versioning" Version="2.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore.Versioning" Version="2.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" />
</ItemGroup> </ItemGroup>

View File

@ -1,173 +1,77 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Mirea.Api.DataAccess.Application; using Mirea.Api.DataAccess.Application;
using Mirea.Api.DataAccess.Persistence; using Mirea.Api.DataAccess.Persistence;
using Mirea.Api.DataAccess.Persistence.Common; using Mirea.Api.DataAccess.Persistence.Common;
using Mirea.Api.Endpoint.Common.Interfaces; using Mirea.Api.Endpoint.Common.Interfaces;
using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Common.Services.Security; using Mirea.Api.Endpoint.Configuration.AppConfig;
using Mirea.Api.Endpoint.Configuration;
using Mirea.Api.Endpoint.Configuration.General; using Mirea.Api.Endpoint.Configuration.General;
using Mirea.Api.Endpoint.Configuration.General.Settings; using Mirea.Api.Endpoint.Configuration.General.Settings;
using Mirea.Api.Endpoint.Configuration.General.Validators; using Mirea.Api.Endpoint.Configuration.General.Validators;
using Mirea.Api.Endpoint.Configuration.Swagger;
using Mirea.Api.Endpoint.Middleware; using Mirea.Api.Endpoint.Middleware;
using Mirea.Api.Security.Common.Interfaces;
using Swashbuckle.AspNetCore.SwaggerGen;
using System; using System;
using System.Collections;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
namespace Mirea.Api.Endpoint; namespace Mirea.Api.Endpoint;
public class Program public class Program
{ {
private static IConfigurationRoot ConfigureEnvironment() public static IServiceCollection AddDatabase(IServiceCollection services, IConfiguration configuration)
{ {
EnvironmentManager.LoadEnvironment(".env"); var dbSettings = configuration.Get<DbSettings>();
var environmentVariables = Environment.GetEnvironmentVariables() services.AddApplication();
.OfType<DictionaryEntry>() services.AddPersistence(
.ToDictionary( dbSettings?.DatabaseProvider ?? DatabaseProvider.Sqlite,
entry => entry.Key.ToString() ?? string.Empty, dbSettings?.ConnectionStringSql ?? string.Empty);
entry => entry.Value?.ToString() ?? string.Empty
);
var result = new ConfigurationBuilder().AddInMemoryCollection(environmentVariables!);
return result.Build();
}
private static IServiceCollection ConfigureJwtToken(IServiceCollection services, IConfiguration configuration)
{
var lifeTimeJwt = TimeSpan.FromMinutes(int.Parse(configuration["SECURITY_LIFE_TIME_JWT"]!));
var jwtDecrypt = Encoding.UTF8.GetBytes(configuration["SECURITY_ENCRYPTION_TOKEN"] ?? string.Empty);
if (jwtDecrypt.Length != 32)
throw new InvalidOperationException("The secret token \"SECURITY_ENCRYPTION_TOKEN\" cannot be less than 32 characters long. Now the size is equal is " + jwtDecrypt.Length);
var jwtKey = Encoding.UTF8.GetBytes(configuration["SECURITY_SIGNING_TOKEN"] ?? string.Empty);
if (jwtKey.Length != 64)
throw new InvalidOperationException("The signature token \"SECURITY_SIGNING_TOKEN\" cannot be less than 64 characters. Now the size is " + jwtKey.Length);
var jwtIssuer = configuration["SECURITY_JWT_ISSUER"];
var jwtAudience = configuration["SECURITY_JWT_AUDIENCE"];
if (string.IsNullOrEmpty(jwtAudience) || string.IsNullOrEmpty(jwtIssuer))
throw new InvalidOperationException("The \"SECURITY_JWT_ISSUER\" and \"SECURITY_JWT_AUDIENCE\" are not specified");
services.AddSingleton<IAccessToken, JwtTokenService>(_ => new JwtTokenService
{
Audience = jwtAudience,
Issuer = jwtIssuer,
Lifetime = lifeTimeJwt,
EncryptionKey = jwtDecrypt,
SigningKey = jwtKey
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtIssuer,
ValidateAudience = true,
ValidAudience = jwtAudience,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(jwtKey),
TokenDecryptionKey = new SymmetricSecurityKey(jwtDecrypt)
};
});
return services; return services;
} }
private static IServiceCollection ConfigureSecurity(IServiceCollection services)
{
services.AddSingleton<IAccessToken, JwtTokenService>();
services.AddSingleton<IRevokedToken, MemoryRevokedTokenService>();
return services;
}
public static void Main(string[] args) public static void Main(string[] args)
{ {
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddConfiguration(ConfigureEnvironment()); builder.Configuration.AddConfiguration(EnvironmentConfiguration.GetEnvironment());
builder.Configuration.AddJsonFile(PathBuilder.Combine(GeneralConfig.FilePath), optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile(PathBuilder.Combine(GeneralConfig.FilePath), optional: true, reloadOnChange: true);
builder.Services.Configure<GeneralConfig>(builder.Configuration); builder.Services.Configure<GeneralConfig>(builder.Configuration);
var generalConfig = builder.Configuration.Get<GeneralConfig>(); builder.Host.AddCustomSerilog();
builder.Services.AddApplication(); AddDatabase(builder.Services, builder.Configuration);
builder.Services.AddPersistence(generalConfig?.DbSettings?.DatabaseProvider ?? DatabaseProvider.Sqlite, generalConfig?.DbSettings?.ConnectionStringSql ?? string.Empty);
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddSingleton<IMaintenanceModeNotConfigureService, MaintenanceModeNotConfigureService>(); builder.Services.AddSingleton<IMaintenanceModeNotConfigureService, MaintenanceModeNotConfigureService>();
builder.Services.AddSingleton<IMaintenanceModeService, MaintenanceModeService>(); builder.Services.AddSingleton<IMaintenanceModeService, MaintenanceModeService>();
builder.Services.AddSingleton<ISetupToken, SetupTokenService>(); builder.Services.AddSingleton<ISetupToken, SetupTokenService>();
builder.Services.AddMemoryCache();
builder.Services.AddCustomRedis(builder.Configuration);
builder.Services.AddCors(options => builder.Services.AddCors(options =>
{ {
options.AddPolicy("AllowAll", policy => options.AddPolicy("AllowAll", policy =>
{ {
policy.AllowAnyHeader(); policy.AllowAnyHeader();
policy.AllowAnyMethod(); policy.AllowAnyMethod();
policy.AllowAnyOrigin(); policy.WithOrigins("http://localhost:4200");
policy.AllowCredentials();
}); });
}); });
builder.Services.AddApiVersioning(options => builder.Services.AddCustomApiVersioning();
{ builder.Services.AddCustomSwagger();
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
});
builder.Services.AddVersionedApiExplorer(options => builder.Services.AddJwtToken(builder.Configuration);
{ builder.Services.AddSecurity(builder.Configuration);
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.OperationFilter<SwaggerDefaultValues>();
var basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory);
var xmlPath = Path.Combine(basePath, "docs.xml");
options.IncludeXmlComments(xmlPath);
});
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
var app = builder.Build(); var app = builder.Build();
#if DEBUG app.UseCors("AllowAll");
// Write configurations app.UseCustomSerilog();
foreach (var item in app.Configuration.AsEnumerable())
Console.WriteLine($"{item.Key}:{item.Value}");
#endif
using (var scope = app.Services.CreateScope()) using (var scope = app.Services.CreateScope())
{ {
@ -184,27 +88,11 @@ public class Program
maintenanceModeService.DisableMaintenanceMode(); maintenanceModeService.DisableMaintenanceMode();
DbInitializer.Initialize(uberDbContext); DbInitializer.Initialize(uberDbContext);
// todo: if admin not found
} }
} }
// Configure the HTTP request pipeline. app.UseCustomSwagger(app.Services);
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var provider = app.Services.GetService<IApiVersionDescriptionProvider>();
foreach (var description in provider!.ApiVersionDescriptions)
{
var url = $"/swagger/{description.GroupName}/swagger.json";
var name = description.GroupName.ToUpperInvariant();
options.SwaggerEndpoint(url, name);
}
});
}
app.UseMiddleware<MaintenanceModeMiddleware>(); app.UseMiddleware<MaintenanceModeMiddleware>();
app.UseMiddleware<CustomExceptionHandlerMiddleware>(); app.UseMiddleware<CustomExceptionHandlerMiddleware>();
@ -212,7 +100,6 @@ public class Program
app.UseAuthorization(); app.UseAuthorization();
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();

View File

@ -16,7 +16,7 @@
<PackageReference Include="FluentValidation" Version="11.9.1" /> <PackageReference Include="FluentValidation" Version="11.9.1" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.1" /> <PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.1" />
<PackageReference Include="MediatR" Version="12.2.0" /> <PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>