modules/backend/Write-InfrastructureLayer.ps1

param(
    [string]$ProjectName,
    [string]$OrmMode,
    [string]$OutputPath,
    [bool]$Observability = $false
)

. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Utils.ps1")

$base = Join-Path $OutputPath "src\$ProjectName.Infrastructure"

function Write-File($rel, $con) { Write-CoreFile (Join-Path $base $rel) $con }
function T($t) { $t.Replace("{P}", $ProjectName) }

# ===== TokenService =====
Write-File "Services\TokenService.cs" (T @'
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using {P}.Application.Interfaces;
 
namespace {P}.Infrastructure.Services;
 
public class TokenService : ITokenService
{
    private readonly IConfiguration _config;
 
    public TokenService(IConfiguration config) => _config = config;
 
    public string GenerateToken(int userId, string username, string role = "User")
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var expires = DateTime.UtcNow.AddHours(8);
 
        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
            new Claim(ClaimTypes.Name, username),
            new Claim(ClaimTypes.Role, role)
        };
 
        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: expires,
            signingCredentials: creds);
 
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}
'@
)

# ===== BcryptPasswordHasher =====
Write-File "Services\BcryptPasswordHasher.cs" (T @'
using {P}.Application.Interfaces;
 
namespace {P}.Infrastructure.Services;
 
public class BcryptPasswordHasher : IPasswordHasher
{
    public string Hash(string password) =>
        BCrypt.Net.BCrypt.HashPassword(password, workFactor: 11);
 
    public bool Verify(string password, string hash) =>
        BCrypt.Net.BCrypt.Verify(password, hash);
}
'@
)

if ($Observability) {
    # ===== SerilogConfiguration =====
    Write-File "Configurations\SerilogConfiguration.cs" (T @'
using Microsoft.AspNetCore.Builder;
using Serilog;
 
namespace {P}.Infrastructure.Configurations;
 
public static class SerilogConfiguration
{
    public static void AddSerilogConfiguration(this ConfigureHostBuilder host)
    {
        host.UseSerilog((ctx, lc) => lc
            .WriteTo.Console()
            .ReadFrom.Configuration(ctx.Configuration));
    }
}
'@
)

}


# ===== ServiceRegistration =====
$obsUsing = if ($Observability) { "using {P}.Infrastructure.Configurations;" } else { "" }
Write-File "ServiceRegistration\InfrastructureServiceRegistration.cs" (T @"
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using {P}.Application.Interfaces;
using {P}.Infrastructure.Services;
$obsUsing
 
namespace {P}.Infrastructure.ServiceRegistration;
 
public static class InfrastructureServiceRegistration
{
    public static IServiceCollection AddInfrastructureServices(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        services.AddScoped<ITokenService, TokenService>();
        services.AddScoped<IPasswordHasher, BcryptPasswordHasher>();
 
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
 
            .AddJwtBearer(opt =>
            {
                opt.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = configuration["Jwt:Issuer"],
                    ValidAudience = configuration["Jwt:Audience"],
                    IssuerSigningKey = new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(configuration["Jwt:Key"]!))
                };
            });
 
        return services;
    }
}
"@
)