Files
2026-08-25 16:46:05 +05:30

211 lines
9.3 KiB
C#

using System.Security.Claims;
using Distribution.Data;
using Distribution.Features.Auth;
using Distribution.Features.Brand;
using Distribution.Features.Category;
using Distribution.Features.DeliveryRide;
using Distribution.Features.Payment;
using Distribution.Features.DriverComplianceConfig;
using Distribution.Features.DriverProfile;
using Distribution.Features.Grn;
using Distribution.Features.Item;
using Distribution.Features.Order;
using Distribution.Features.PriceList;
using Distribution.Features.Po;
using Distribution.Features.PurchaseReturn;
using Distribution.Features.Returns;
using Distribution.Features.Claims;
using Distribution.Features.SupplierClaims;
using Distribution.Features.Region;
using Distribution.Features.LoadingEvent;
using Distribution.Features.Ride;
using Distribution.Features.Role;
using Distribution.Features.Route;
using Distribution.Features.Sale;
using Distribution.Features.Scheme;
using Distribution.Features.Store;
using Distribution.Features.Store.Kyc;
using Distribution.Features.Supplier;
using Distribution.Features.Sync;
using Distribution.Features.Uom;
using Distribution.Features.User;
using Distribution.Features.Vehicle;
using Distribution.Features.Stock;
using Distribution.Features.Warehouse;
using Distribution.Infra.Authorization;
using Distribution.Infra.UoW;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' was not found.");
builder.Services.AddDbContext<AppDBContext>(options =>
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
builder.Services.AddScoped<IUnitOfWork, EFUnitOfWork>();
builder.Services.AddScoped<IRegionRepository, RegionRepository>();
builder.Services.AddScoped<IRouteRepository, RouteRepository>();
builder.Services.AddScoped<IVehicleRepository, VehicleRepository>();
builder.Services.AddScoped<IStoreRepository, StoreRepository>();
builder.Services.AddScoped<IWarehouseRepository, WarehouseRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<ISaleRepository, SaleRepository>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IRoleRepository, RoleRepository>();
builder.Services.AddScoped<ICategoryRepository, CategoryRepository>();
builder.Services.AddScoped<ISubCategoryRepository, SubCategoryRepository>();
builder.Services.AddScoped<IBrandRepository, BrandRepository>();
builder.Services.AddScoped<IUomRepository, UomRepository>();
builder.Services.AddScoped<IItemRepository, ItemRepository>();
builder.Services.AddScoped<IStockRepository, StockRepository>();
builder.Services.AddScoped<IDriverProfileRepository, DriverProfileRepository>();
builder.Services.AddScoped<IDriverComplianceConfigRepository, DriverComplianceConfigRepository>();
builder.Services.AddScoped<IRideRepository, RideRepository>();
builder.Services.AddScoped<IRideCancelRequestRepository, RideCancelRequestRepository>();
builder.Services.AddScoped<ILoadingSheetRepository, LoadingSheetRepository>();
builder.Services.AddScoped<IStoreKycDocumentRepository, StoreKycDocumentRepository>();
builder.Services.AddScoped<IPriceListRepository, PriceListRepository>();
builder.Services.AddScoped<ISupplierRepository, SupplierRepository>();
builder.Services.AddScoped<ISchemeRepository, SchemeRepository>();
builder.Services.AddScoped<IPoRepository, PoRepository>();
builder.Services.AddScoped<IGrnRepository, GrnRepository>();
builder.Services.AddScoped<IPurchaseReturnRepository, PurchaseReturnRepository>();
builder.Services.AddScoped<IReturnRepository, ReturnRepository>();
builder.Services.AddScoped<IClaimRepository, ClaimRepository>();
builder.Services.AddScoped<ISupplierClaimRepository, SupplierClaimRepository>();
builder.Services.AddScoped<IDeliveryTaskRepository, DeliveryTaskRepository>();
builder.Services.AddScoped<IPaymentRepository, PaymentRepository>();
builder.Services.AddScoped<RegionService>();
builder.Services.AddScoped<RouteService>();
builder.Services.AddScoped<VehicleService>();
builder.Services.AddScoped<StoreService>();
builder.Services.AddScoped<WarehouseService>();
builder.Services.AddScoped<OrderService>();
builder.Services.AddScoped<SaleService>();
builder.Services.AddScoped<UserService>();
builder.Services.AddScoped<RoleService>();
builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<CategoryService>();
builder.Services.AddScoped<BrandService>();
builder.Services.AddScoped<UomService>();
builder.Services.AddScoped<ItemService>();
builder.Services.AddScoped<SyncService>();
builder.Services.AddScoped<StockService>();
builder.Services.AddScoped<StoreKycDocumentService>();
builder.Services.AddScoped<PriceListService>();
builder.Services.AddScoped<SupplierService>();
builder.Services.AddScoped<DriverProfileService>();
builder.Services.AddScoped<DriverComplianceConfigService>();
builder.Services.AddScoped<RideService>();
builder.Services.AddScoped<LoadingSheetService>();
builder.Services.AddScoped<SchemeService>();
builder.Services.AddScoped<PoService>();
builder.Services.AddScoped<GrnService>();
builder.Services.AddScoped<PurchaseReturnService>();
builder.Services.AddScoped<ReturnService>();
builder.Services.AddScoped<ClaimService>();
builder.Services.AddScoped<SupplierClaimService>();
builder.Services.AddScoped<DeliveryTaskService>();
builder.Services.AddScoped<PaymentService>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
builder.Services.AddHttpClient<IAuthHexClient, AuthHexClient>(client =>
{
var baseUrl = builder.Configuration["AuthHex:BaseUrl"] ?? throw new InvalidOperationException("AuthHex:BaseUrl is not configured.");
client.BaseAddress = new Uri(baseUrl);
});
builder.Services.AddScoped<IPermissionRepository, PermissionRepository>();
builder.Services.AddScoped<IScopeRepository, ScopeRepository>();
builder.Services.AddScoped<IPermissionService, PermissionService>();
builder.Services.AddScoped<IScopeAccessService, ScopeAccessService>();
builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
var jwtSection = builder.Configuration.GetSection("Jwt");
var jwtPublicKeyXml = jwtSection["PublicKey"] ?? throw new InvalidOperationException("Jwt:PublicKey is not configured.");
var jwtSigningKey = new RsaSecurityKey(RsaXmlKeyReader.ReadPublicKey(jwtPublicKeyXml));
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtSection["Issuer"],
ValidateAudience = true,
ValidAudience = jwtSection["Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = jwtSigningKey,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
// AuthHex issues the token and owns its claim shape; this app's authorization (permission
// policies, role-level checks) runs against the *local* Role/Permission tables, so every
// validated token is mapped back to a local User/Role once here, not duplicated per
// endpoint. A token for a user with no matching local User record is rejected outright —
// login via AuthHex does not implicitly provision a local user.
options.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
var subClaim = context.Principal?.FindFirst("sub")?.Value
?? context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (!Guid.TryParse(subClaim, out var userId))
{
context.Fail("Token is missing a valid user id claim.");
return;
}
var userRepo = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
var user = await userRepo.GetByIdAsync(userId);
if (user is null || !user.IsActive)
{
context.Fail("User not found or inactive.");
return;
}
var identity = (ClaimsIdentity)context.Principal!.Identity!;
identity.AddClaim(new Claim("username", user.Username));
identity.AddClaim(new Claim("role_id", user.FK_RoleId.ToString()));
identity.AddClaim(new Claim("role_level", (user.Role?.Level ?? 0).ToString()));
}
};
});
builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();