Initial commit
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PowderCoating.Core\PowderCoating.Core.csproj" />
|
||||
<ProjectReference Include="..\PowderCoating.Application\PowderCoating.Application.csproj" />
|
||||
<ProjectReference Include="..\PowderCoating.Infrastructure\PowderCoating.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\PowderCoating.Shared\PowderCoating.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,210 @@
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Core.Interfaces;
|
||||
using PowderCoating.Infrastructure.Data;
|
||||
using PowderCoating.Infrastructure.Repositories;
|
||||
using Serilog;
|
||||
using AutoMapper;
|
||||
using PowderCoating.Application.Mappings;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Configure Serilog
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(builder.Configuration)
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File("logs/api-.txt", rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
builder.Host.UseSerilog();
|
||||
|
||||
// Add services to the container
|
||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
||||
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
||||
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseSqlServer(connectionString));
|
||||
|
||||
// Configure Identity
|
||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
||||
{
|
||||
options.Password.RequireDigit = true;
|
||||
options.Password.RequireLowercase = true;
|
||||
options.Password.RequireUppercase = true;
|
||||
options.Password.RequireNonAlphanumeric = true;
|
||||
options.Password.RequiredLength = 12;
|
||||
options.Password.RequiredUniqueChars = 4;
|
||||
options.User.RequireUniqueEmail = true;
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
// Configure JWT Authentication
|
||||
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
|
||||
var secretKey = jwtSettings["SecretKey"] ?? throw new InvalidOperationException("JWT Secret Key not found.");
|
||||
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = jwtSettings["Issuer"],
|
||||
ValidAudience = jwtSettings["Audience"],
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)),
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
});
|
||||
|
||||
// Register repositories and services
|
||||
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||||
|
||||
// Configure AutoMapper
|
||||
builder.Services.AddSingleton<IMapper>(sp =>
|
||||
{
|
||||
var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
|
||||
var config = new MapperConfiguration(cfg =>
|
||||
{
|
||||
cfg.AddProfile(new CustomerProfile());
|
||||
cfg.AddProfile(new JobProfile());
|
||||
}, loggerFactory);
|
||||
return config.CreateMapper();
|
||||
});
|
||||
|
||||
// Add CORS - Configured per environment
|
||||
var allowedOrigins = builder.Configuration.GetSection("CorsSettings:AllowedOrigins").Get<string[]>()
|
||||
?? new[] { "http://localhost:3000" }; // Default for development
|
||||
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowMobileApp",
|
||||
policy =>
|
||||
{
|
||||
policy.WithOrigins(allowedOrigins)
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader()
|
||||
.AllowCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
// Add controllers
|
||||
builder.Services.AddControllers();
|
||||
|
||||
// Configure Swagger/OpenAPI
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = "Powder Coating Management API",
|
||||
Version = "v1",
|
||||
Description = "API for Powder Coating Management System",
|
||||
Contact = new OpenApiContact
|
||||
{
|
||||
Name = "Your Company",
|
||||
Email = "support@yourcompany.com"
|
||||
}
|
||||
});
|
||||
|
||||
// Add JWT Authentication to Swagger
|
||||
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Description = "JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below.",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.ApiKey,
|
||||
Scheme = "Bearer"
|
||||
});
|
||||
|
||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
Array.Empty<string>()
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Global exception handler — returns RFC 7807 Problem Details for all unhandled exceptions
|
||||
app.UseExceptionHandler(exApp => exApp.Run(async ctx =>
|
||||
{
|
||||
ctx.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
ctx.Response.ContentType = "application/problem+json";
|
||||
|
||||
var feature = ctx.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>();
|
||||
if (feature?.Error != null)
|
||||
{
|
||||
var logger = ctx.RequestServices.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(feature.Error, "Unhandled exception in API request {Method} {Path}",
|
||||
ctx.Request.Method, ctx.Request.Path);
|
||||
}
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
type = "https://tools.ietf.org/html/rfc7807",
|
||||
title = "An unexpected error occurred.",
|
||||
status = 500,
|
||||
traceId = System.Diagnostics.Activity.Current?.Id ?? ctx.TraceIdentifier
|
||||
});
|
||||
}));
|
||||
|
||||
// Configure the HTTP request pipeline
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Powder Coating API v1");
|
||||
c.RoutePrefix = string.Empty; // Swagger at root
|
||||
});
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseCors("AllowMobileApp");
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
// Run migrations and seed data
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
var context = services.GetRequiredService<ApplicationDbContext>();
|
||||
await context.Database.MigrateAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(ex, "An error occurred while migrating the database.");
|
||||
}
|
||||
}
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"PowderCoating.Api": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:58459;http://localhost:58460"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "USE_USER_SECRETS_OR_ENVIRONMENT_VARIABLE"
|
||||
},
|
||||
"JwtSettings": {
|
||||
"SecretKey": "USE_USER_SECRETS_OR_ENVIRONMENT_VARIABLE",
|
||||
"Issuer": "PowderCoatingAPI",
|
||||
"Audience": "PowderCoatingMobileApp",
|
||||
"ExpirationMinutes": 15,
|
||||
"RefreshTokenExpirationDays": 7
|
||||
},
|
||||
"CorsSettings": {
|
||||
"AllowedOrigins": [
|
||||
"http://localhost:3000",
|
||||
"http://localhost:5173"
|
||||
]
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Override": {
|
||||
"Microsoft": "Warning",
|
||||
"System": "Warning"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "localhost;127.0.0.1"
|
||||
}
|
||||
Reference in New Issue
Block a user