This commit is contained in:
Thanakarn Klangkasame
2025-09-30 11:01:02 +07:00
commit 92e614674c
182 changed files with 9596 additions and 0 deletions

141
AMREZ.EOP.API/Program.cs Normal file
View File

@@ -0,0 +1,141 @@
using AMREZ.EOP.Abstractions.Applications.Tenancy;
using AMREZ.EOP.API.Middleware;
using AMREZ.EOP.Domain.Shared.Contracts;
using AMREZ.EOP.Domain.Shared.Tenancy;
using AMREZ.EOP.Infrastructures.Data;
using AMREZ.EOP.Infrastructures.DependencyInjections;
using AMREZ.EOP.Infrastructures.Options;
using AMREZ.EOP.Infrastructures.Tenancy;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Npgsql;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<AuthOptions>(builder.Configuration.GetSection("Connections"));
builder.Services.AddInfrastructure();
builder.Services.AddOpenApi();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "AMREZ.EOP API",
Version = "v1"
});
// Cookie auth (ชื่อคุกกี้ eop.auth)
c.AddSecurityDefinition("cookieAuth", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Cookie,
Name = "eop.auth",
Description = "Sign in via /api/authentication/login to receive cookie."
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{ Type = ReferenceType.SecurityScheme, Id = "cookieAuth" }
},
Array.Empty<string>()
}
});
});
builder.Services
.AddAuthentication(AuthPolicies.Scheme)
.AddCookie(AuthPolicies.Scheme, o => { o.LoginPath="/api/authentication/login"; o.Cookie.Name="eop.auth"; o.SlidingExpiration=true; });
builder.Services.AddAuthorization();
builder.Services.AddSingleton(new StrictTenantGuardOptions
{
PlatformSlug = "public",
HeaderName = "X-Tenant",
ClaimName = "tenant"
});
builder.Services.AddTransient<StrictTenantGuardMiddleware>();
builder.Services.AddControllers();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
await ApplyMigrationsAsync(app.Services);
app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI(o =>
{
o.SwaggerEndpoint("/swagger/v1/swagger.json", "AMREZ.EOP API v1");
o.RoutePrefix = "swagger";
o.DisplayRequestDuration();
});
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMiddleware<StrictTenantGuardMiddleware>();
app.UseAuthorization();
app.MapControllers();
app.Run();
static async Task ApplyMigrationsAsync(IServiceProvider services)
{
using var scope = services.CreateScope();
var sp = scope.ServiceProvider;
var opts = sp.GetRequiredService<Microsoft.Extensions.Options.IOptions<AuthOptions>>().Value;
var factory = sp.GetRequiredService<ITenantDbContextFactory>();
if (!opts.UseSchemaPerTenant)
{
await using var db = factory.Create<AppDbContext>(new TenantContext { Id = "_shared_" });
await db.Database.MigrateAsync();
}
var schemas = await GetAppSchemasAsync(opts.DefaultConnection);
foreach (var schema in schemas)
{
var t = new TenantContext { Id = schema, Schema = schema, Mode = TenantMode.Schema };
await using var db = factory.Create<AppDbContext>(t);
await db.Database.MigrateAsync();
}
}
static async Task<List<string>> GetAppSchemasAsync(string connectionString)
{
var list = new List<string>();
await using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync();
const string sql = @"
SELECT nspname
FROM pg_namespace
WHERE nspname NOT LIKE 'pg_%'
AND nspname <> 'information_schema'
ORDER BY nspname;";
await using var cmd = new NpgsqlCommand(sql, conn);
await using var rd = await cmd.ExecuteReaderAsync();
while (await rd.ReadAsync())
list.Add(rd.GetString(0));
if (list.Count == 0) list.Add("public");
return list;
}