Reorganize root folder structure: Remove apps layer layer

This commit is contained in:
Халимов Рустам
2026-03-19 22:22:45 +03:00
parent fb252f9d87
commit 11ebbc853b
296 changed files with 139 additions and 139 deletions

210
backend/src/Host/Program.cs Normal file
View File

@@ -0,0 +1,210 @@
using Carter;
using Knot.Shared.Infrastructure;
using Knot.Modules.Identity;
using Knot.Modules.Chats;
using Knot.Modules.Chats.Infrastructure.SignalR;
using Knot.Modules.Identity.Infrastructure.Persistence;
using Knot.Modules.Chats.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.SignalR;
using System.Security.Claims;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var builder = WebApplication.CreateBuilder(args);
// Регистрация модулей
// Маппинг стандартных переменных окружения в иерархию .NET
var envMappings = new Dictionary<string, string?>
{
["ConnectionStrings:DefaultConnection"] = builder.Configuration["DATABASE_URL"],
["ConnectionStrings:MongoConnection"] = builder.Configuration["MONGO_CONNECTION"],
["Jwt:Secret"] = builder.Configuration["JWT_SECRET"],
["Jwt:Issuer"] = builder.Configuration["JWT_ISSUER"],
["Jwt:Audience"] = builder.Configuration["JWT_AUDIENCE"],
["WebRtc:TurnUrl"] = builder.Configuration["TURN_URL"],
["WebRtc:TurnUsername"] = builder.Configuration["TURN_USERNAME"],
["WebRtc:TurnPassword"] = builder.Configuration["TURN_PASSWORD"],
["KNOT_MASTER_ENCRYPTION_KEY"] = builder.Configuration["KNOT_MASTER_ENCRYPTION_KEY"],
["S3_ENDPOINT"] = builder.Configuration["S3_ENDPOINT"],
["S3_ACCESS_KEY"] = builder.Configuration["S3_ACCESS_KEY"],
["S3_SECRET_KEY"] = builder.Configuration["S3_SECRET_KEY"],
["S3_BUCKET"] = builder.Configuration["S3_BUCKET"],
["KNOT_ADMIN_USER"] = builder.Configuration["KNOT_ADMIN_USER"],
["KNOT_ADMIN_PASSWORD"] = builder.Configuration["KNOT_ADMIN_PASSWORD"]
};
// Добавляем только те, что реально заданы в ENV
builder.Configuration.AddInMemoryCollection(
envMappings.Where(kv => !string.IsNullOrEmpty(kv.Value))
.ToDictionary(kv => kv.Key, kv => kv.Value));
builder.Services.AddIdentityModule(builder.Configuration);
builder.Services.AddChatsModule(builder.Configuration);
builder.Services.AddSharedInfrastructure(builder.Configuration);
// CQRS / MediatR для команд в Host (например, AdminController)
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
// Carter для вызова Minimal APIs (Endpoints)
builder.Services.AddCarter();
// Настройка CORS
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
var originsFromConfig = builder.Configuration["Cors:Origins"];
var domain = builder.Configuration["DOMAIN"];
var origins = !string.IsNullOrEmpty(originsFromConfig)
? originsFromConfig.Split(',')
: (!string.IsNullOrEmpty(domain) ? new[] { $"https://{domain}" } : new[] { "*" });
var corsBuilder = policy.WithOrigins(origins)
.AllowAnyHeader()
.AllowAnyMethod();
if (origins.Contains("*"))
{
// The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time.
}
else
{
corsBuilder.AllowCredentials();
}
});
});
// Настройка Swagger/OpenAPI
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Настройка маршрутизации
builder.Services.AddRouting(options =>
{
options.LowercaseUrls = true;
options.LowercaseQueryStrings = true;
});
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient();
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
builder.Services.AddSignalR()
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
builder.Services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
// Настройка JWT Аутентификации
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"]!))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
// Применяем миграции при старте
using (var scope = app.Services.CreateScope())
{
var identityDb = scope.ServiceProvider.GetRequiredService<IdentityDbContext>();
await identityDb.Database.MigrateAsync();
var chatsDb = scope.ServiceProvider.GetRequiredService<ChatsDbContext>();
await chatsDb.Database.MigrateAsync();
var systemDb = scope.ServiceProvider.GetRequiredService<Knot.Shared.Infrastructure.Persistence.SystemDbContext>();
await systemDb.Database.MigrateAsync();
// Set Encryption Service for MongoDB serializers
var encryptionService = scope.ServiceProvider.GetRequiredService<Knot.Shared.Kernel.Security.IEncryptionService>();
Knot.Modules.Chats.Infrastructure.Persistence.Mongo.EncryptedStringSerializer.EncryptionService = encryptionService;
// Initialize Global Settings Cache
var settingsService = scope.ServiceProvider.GetRequiredService<Knot.Shared.Kernel.Configuration.ISettingsService>();
if (settingsService is Knot.Shared.Infrastructure.Configuration.SettingsService concreteSettings)
{
concreteSettings.Initialize();
}
}
// Настройка конвейера запросов
app.UseMiddleware<Knot.Shared.Infrastructure.Middleware.ExceptionHandlingMiddleware>();
app.UseMiddleware<Knot.Shared.Infrastructure.Middleware.AdminAuthMiddleware>();
app.UseCors();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// app.UseHttpsRedirection();
// Не раздаем статические файлы, так как теперь используем MinIO
app.UseAuthentication();
app.UseAuthorization();
// Добавляем контроллеры и Carter (Minimal APIs)
app.MapControllers();
app.MapCarter();
// Добавляем SignalR хабы
app.MapHub<ChatHub>("/hubs/chat");
app.Run();
public class CustomUserIdProvider : IUserIdProvider
{
public string? GetUserId(HubConnectionContext connection)
{
return connection.User?.FindFirstValue("sub") ?? connection.User?.FindFirstValue(ClaimTypes.NameIdentifier);
}
}