using System.CommandLine;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using OptixServe.Api.Configuration;
using OptixServe.Api.Endpoints;
using OptixServe.Api.Services;
using OptixServe.Application.Services;
using OptixServe.Infrastructure.Configuration;
using OptixServe.Infrastructure.Data;
using OptixServe.Infrastructure.Utilites;
class Program
{
///
/// Main method that configures and runs the application
///
/// Command line arguments
/// A Task representing the asynchronous operation
static async Task Main(string[] args)
{
var rootCommand = new RootCommand("OptixServe API");
// Configure the --config/-c command line option
var configOption = new Option("--config", "-c")
{
Description = "Config file path",
CustomParser = arg =>
{
if (!arg.Tokens.Any()) return null;
var filePath = arg.Tokens.Single().Value;
if (!File.Exists(filePath))
{
arg.AddError($"Configuration file not found: {filePath}");
return null;
}
return new FileInfo(filePath);
}
};
rootCommand.Add(configOption);
// Set handle for rootCommand
// The handle prototype and way of parsing arguments
// is changed in System.CommandLine 2.0.0-beta5
rootCommand.SetAction((ParseResult parseResult) =>
{
var builder = WebApplication.CreateSlimBuilder(args);
var configFile = parseResult.GetValue(configOption);
builder.AddConfigurationWithCommand(configFile);
builder.RegisterServices();
builder.RegiserJsonContext();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
using (var scope = app.Services.CreateScope())
{
var initializer = scope.ServiceProvider.GetRequiredService();
initializer.Initialize();
}
var apiGroup = app.MapGroup("api/v1");
StartupHelper.RegisterEndpoints(apiGroup);
app.Run();
});
// Parse arguments then invoke the command
var parseResult = rootCommand.Parse(args);
await parseResult.InvokeAsync();
}
}
///
/// Contains extension methods for WebApplicationBuilder and WebApplication
///
static class StartupHelper
{
///
/// Adds configuration sources to the application builder
///
/// WebApplicationBuilder instance
/// Optional configuration file specified via command line
public static void AddConfigurationWithCommand(this WebApplicationBuilder builder, FileInfo? file)
{
// Configure configuration sources in specified order
var configurationBuilder = ConfigurationHelper.CreateDefaultBuilder();
// Add command-line specified config file if provided
if (file != null)
{
configurationBuilder.AddJsonFile(file.FullName, optional: false);
}
builder.Configuration.AddConfiguration(configurationBuilder.Build());
}
///
/// Configures DbContext services
///
///
///
///
public static IServiceCollection AddAppDatabase(this IServiceCollection services, DatabaseSettings dbSettings)
{
services.AddDbContext(options => DatabaseHelper.ConfigureDbContext(options, dbSettings));
return services;
}
///
/// Configures services for DI
///
/// WebApplicationBuilder instance
public static void RegisterServices(this WebApplicationBuilder builder)
{
// Add configuration classes
var apiConfigSection = builder.Configuration.GetSection("OptixServe:Api");
builder.Services.Configure(apiConfigSection);
var apiConfig = apiConfigSection.Get();
var infraConfigSection = builder.Configuration.GetSection("OptixServe:Infrastructure");
builder.Services.Configure(infraConfigSection);
var infraConfig = infraConfigSection.Get();
// Add DBContext class
builder.Services.AddAppDatabase(infraConfig?.Database!);
builder.Services.AddScoped();
// Application services
builder.Services.AddScoped();
builder.Services.AddScoped();
// Add Authentication and Authorization
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var jwtSettings = apiConfig?.Jwt ?? throw new ArgumentNullException(nameof(builder), "JWT settings are not configured.");
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.Issuer,
ValidAudience = jwtSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Secret))
};
});
builder.Services.AddAuthorization();
}
///
/// Configures JSON serialization options with custom context
///
/// WebApplicationBuilder instance
public static void RegiserJsonContext(this WebApplicationBuilder builder)
{
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolverChain.Add(UserJsonContext.Default);
options.SerializerOptions.TypeInfoResolverChain.Add(VersionJsonContext.Default);
});
}
///
/// Registers all API endpoints
///
/// Root RouteGroupBuilder instance
public static void RegisterEndpoints(RouteGroupBuilder rootGroup)
{
UserEndpoint.Register(rootGroup);
VersionEndpoint.Register(rootGroup);
}
}