UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

1,568 lines (1,344 loc) 56.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.aspnetCoreWebApiTemplate = void 0; exports.aspnetCoreWebApiTemplate = { id: 'aspnet-core-webapi', name: 'ASP.NET Core Web API', displayName: 'ASP.NET Core Web API', language: 'csharp', framework: 'aspnet-core-webapi', description: 'Enterprise-grade ASP.NET Core Web API with controllers, dependency injection, Entity Framework, JWT authentication, and comprehensive testing', version: '1.0.0', tags: ['csharp', 'dotnet', 'aspnet-core', 'web-api', 'enterprise', 'rest-api', 'jwt', 'entity-framework', 'swagger'], port: 5000, files: { // Project file with all required packages [`\${projectName}.csproj`]: `<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <GenerateDocumentationFile>true</GenerateDocumentationFile> <DocumentationFile>bin\\Debug\\net8.0\\\${projectName}.xml</DocumentationFile> <NoWarn>\$(NoWarn);1591</NoWarn> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Serilog.AspNetCore" Version="8.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="AutoMapper" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" /> <PackageReference Include="StackExchange.Redis" Version="2.7.10" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" /> </ItemGroup> </Project>`, // Program.cs - Main entry point with comprehensive configuration 'Program.cs': `using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Serilog; using System.Reflection; using System.Text; using \${projectName}.Data; using \${projectName}.Models; using \${projectName}.Services; using \${projectName}.Middleware; using \${projectName}.Extensions; // Configure Serilog Log.Logger = new LoggerConfiguration() .WriteTo.Console() .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); try { var builder = WebApplication.CreateBuilder(args); // Add Serilog builder.Host.UseSerilog(); // Add services to the container builder.Services.AddControllers(); // Database context builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // Identity builder.Services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); // JWT Authentication var jwtSettings = builder.Configuration.GetSection("JwtSettings"); 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(jwtSettings["SecretKey"]!)) }; }); // AutoMapper builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly()); // Business services builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IProductService, ProductService>(); builder.Services.AddScoped<ITokenService, TokenService>(); // Redis cache builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("Redis"); }); // API versioning builder.Services.AddApiVersioning(opt => { opt.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0); opt.AssumeDefaultVersionWhenUnspecified = true; opt.ApiVersionReader = Microsoft.AspNetCore.Mvc.ApiVersionReader.Combine( new Microsoft.AspNetCore.Mvc.QueryStringApiVersionReader("apiVersion"), new Microsoft.AspNetCore.Mvc.HeaderApiVersionReader("X-Version"), new Microsoft.AspNetCore.Mvc.UrlSegmentApiVersionReader() ); }); builder.Services.AddVersionedApiExplorer(setup => { setup.GroupNameFormat = "'v'VVV"; setup.SubstituteApiVersionInUrl = true; }); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "\${projectName} API", Version = "v1", Description = "A comprehensive ASP.NET Core Web API with authentication and CRUD operations", Contact = new OpenApiContact { Name = "API Support", Email = "support@\${projectName.toLowerCase()}.com" } }); // Set the comments path for the Swagger JSON and UI var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); // Add JWT authentication c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme. Example: \\"Authorization: Bearer {token}\\"", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, Scheme = "Bearer" }); c.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }, Scheme = "oauth2", Name = "Bearer", In = ParameterLocation.Header, }, new List<string>() } }); }); // CORS builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", policy => { policy.WithOrigins("http://localhost:3000", "https://localhost:3000") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); var app = builder.Build(); // Configure the HTTP request pipeline if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "\${projectName} API V1"); c.RoutePrefix = string.Empty; // Set Swagger UI at app's root }); } // Custom middleware app.UseMiddleware<ExceptionHandlingMiddleware>(); app.UseMiddleware<RequestLoggingMiddleware>(); app.UseHttpsRedirection(); app.UseCors("AllowSpecificOrigin"); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); // Ensure database is created using (var scope = app.Services.CreateScope()) { var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); context.Database.EnsureCreated(); } app.Run(); } catch (Exception ex) { Log.Fatal(ex, "Application terminated unexpectedly"); } finally { Log.CloseAndFlush(); }`, // Database context 'Data/ApplicationDbContext.cs': `using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using \${projectName}.Models; namespace \${projectName}.Data; public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Product> Products { get; set; } public DbSet<Category> Categories { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderItem> OrderItems { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Product entity configuration modelBuilder.Entity<Product>(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Name).IsRequired().HasMaxLength(200); entity.Property(e => e.Description).HasMaxLength(1000); entity.Property(e => e.Price).HasColumnType("decimal(18,2)"); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); entity.HasOne(p => p.Category) .WithMany(c => c.Products) .HasForeignKey(p => p.CategoryId); }); // Category entity configuration modelBuilder.Entity<Category>(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Name).IsRequired().HasMaxLength(100); entity.Property(e => e.Description).HasMaxLength(500); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); }); // Order entity configuration modelBuilder.Entity<Order>(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.TotalAmount).HasColumnType("decimal(18,2)"); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); entity.HasOne(o => o.User) .WithMany() .HasForeignKey(o => o.UserId); }); // OrderItem entity configuration modelBuilder.Entity<OrderItem>(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.UnitPrice).HasColumnType("decimal(18,2)"); entity.Property(e => e.TotalPrice).HasColumnType("decimal(18,2)"); entity.HasOne(oi => oi.Order) .WithMany(o => o.OrderItems) .HasForeignKey(oi => oi.OrderId); entity.HasOne(oi => oi.Product) .WithMany() .HasForeignKey(oi => oi.ProductId); }); // Seed data modelBuilder.Entity<Category>().HasData( new Category { Id = 1, Name = "Electronics", Description = "Electronic devices and accessories" }, new Category { Id = 2, Name = "Clothing", Description = "Apparel and fashion items" }, new Category { Id = 3, Name = "Books", Description = "Books and educational materials" } ); modelBuilder.Entity<Product>().HasData( new Product { Id = 1, Name = "Laptop", Description = "High-performance laptop for development", Price = 1299.99m, CategoryId = 1, Stock = 50, IsActive = true, CreatedAt = DateTime.UtcNow }, new Product { Id = 2, Name = "T-Shirt", Description = "Comfortable cotton t-shirt", Price = 29.99m, CategoryId = 2, Stock = 100, IsActive = true, CreatedAt = DateTime.UtcNow } ); } }`, // Models 'Models/ApplicationUser.cs': `using Microsoft.AspNetCore.Identity; namespace \${projectName}.Models; public class ApplicationUser : IdentityUser { public string? FirstName { get; set; } public string? LastName { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? LastLoginAt { get; set; } public bool IsActive { get; set; } = true; }`, 'Models/Product.cs': `using System.ComponentModel.DataAnnotations; namespace \${projectName}.Models; public class Product { public int Id { get; set; } [Required] [StringLength(200)] public string Name { get; set; } = string.Empty; [StringLength(1000)] public string? Description { get; set; } [Required] [Range(0.01, double.MaxValue, ErrorMessage = "Price must be greater than 0")] public decimal Price { get; set; } [Range(0, int.MaxValue)] public int Stock { get; set; } public bool IsActive { get; set; } = true; [Required] public int CategoryId { get; set; } public Category? Category { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } }`, 'Models/Category.cs': `using System.ComponentModel.DataAnnotations; namespace \${projectName}.Models; public class Category { public int Id { get; set; } [Required] [StringLength(100)] public string Name { get; set; } = string.Empty; [StringLength(500)] public string? Description { get; set; } public bool IsActive { get; set; } = true; public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } public virtual ICollection<Product> Products { get; set; } = new List<Product>(); }`, 'Models/Order.cs': `using System.ComponentModel.DataAnnotations; namespace \${projectName}.Models; public class Order { public int Id { get; set; } [Required] public string UserId { get; set; } = string.Empty; public ApplicationUser? User { get; set; } [Required] public decimal TotalAmount { get; set; } public OrderStatus Status { get; set; } = OrderStatus.Pending; public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>(); } public enum OrderStatus { Pending = 0, Processing = 1, Shipped = 2, Delivered = 3, Cancelled = 4 }`, 'Models/OrderItem.cs': `using System.ComponentModel.DataAnnotations; namespace \${projectName}.Models; public class OrderItem { public int Id { get; set; } [Required] public int OrderId { get; set; } public Order? Order { get; set; } [Required] public int ProductId { get; set; } public Product? Product { get; set; } [Required] [Range(1, int.MaxValue)] public int Quantity { get; set; } [Required] public decimal UnitPrice { get; set; } [Required] public decimal TotalPrice { get; set; } }`, // DTOs 'DTOs/UserRegisterDto.cs': `using System.ComponentModel.DataAnnotations; namespace \${projectName}.DTOs; public class UserRegisterDto { [Required] [EmailAddress] public string Email { get; set; } = string.Empty; [Required] [StringLength(100, MinimumLength = 6)] public string Password { get; set; } = string.Empty; [Required] [Compare("Password")] public string ConfirmPassword { get; set; } = string.Empty; [Required] [StringLength(50)] public string FirstName { get; set; } = string.Empty; [Required] [StringLength(50)] public string LastName { get; set; } = string.Empty; }`, 'DTOs/UserLoginDto.cs': `using System.ComponentModel.DataAnnotations; namespace \${projectName}.DTOs; public class UserLoginDto { [Required] [EmailAddress] public string Email { get; set; } = string.Empty; [Required] public string Password { get; set; } = string.Empty; }`, 'DTOs/ProductDto.cs': `using System.ComponentModel.DataAnnotations; namespace \${projectName}.DTOs; public class ProductCreateDto { [Required] [StringLength(200)] public string Name { get; set; } = string.Empty; [StringLength(1000)] public string? Description { get; set; } [Required] [Range(0.01, double.MaxValue)] public decimal Price { get; set; } [Range(0, int.MaxValue)] public int Stock { get; set; } [Required] public int CategoryId { get; set; } } public class ProductUpdateDto : ProductCreateDto { public bool IsActive { get; set; } = true; } public class ProductResponseDto { public int Id { get; set; } public string Name { get; set; } = string.Empty; public string? Description { get; set; } public decimal Price { get; set; } public int Stock { get; set; } public bool IsActive { get; set; } public int CategoryId { get; set; } public string? CategoryName { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } }`, // Services 'Services/IUserService.cs': `using \${projectName}.DTOs; using \${projectName}.Models; namespace \${projectName}.Services; public interface IUserService { Task<ApplicationUser?> RegisterAsync(UserRegisterDto dto); Task<ApplicationUser?> LoginAsync(UserLoginDto dto); Task<ApplicationUser?> GetByIdAsync(string id); Task<ApplicationUser?> GetByEmailAsync(string email); Task<bool> UpdateAsync(string id, ApplicationUser user); Task<bool> DeleteAsync(string id); Task<IEnumerable<ApplicationUser>> GetAllAsync(); }`, 'Services/UserService.cs': `using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using \${projectName}.DTOs; using \${projectName}.Models; namespace \${projectName}.Services; public class UserService : IUserService { private readonly UserManager<ApplicationUser> _userManager; private readonly SignInManager<ApplicationUser> _signInManager; private readonly ILogger<UserService> _logger; public UserService( UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, ILogger<UserService> logger) { _userManager = userManager; _signInManager = signInManager; _logger = logger; } public async Task<ApplicationUser?> RegisterAsync(UserRegisterDto dto) { try { var user = new ApplicationUser { UserName = dto.Email, Email = dto.Email, FirstName = dto.FirstName, LastName = dto.LastName, CreatedAt = DateTime.UtcNow }; var result = await _userManager.CreateAsync(user, dto.Password); if (result.Succeeded) { _logger.LogInformation("User {Email} registered successfully", dto.Email); return user; } foreach (var error in result.Errors) { _logger.LogWarning("Registration error for {Email}: {Error}", dto.Email, error.Description); } return null; } catch (Exception ex) { _logger.LogError(ex, "Error registering user {Email}", dto.Email); return null; } } public async Task<ApplicationUser?> LoginAsync(UserLoginDto dto) { try { var user = await _userManager.FindByEmailAsync(dto.Email); if (user == null || !user.IsActive) { return null; } var result = await _signInManager.CheckPasswordSignInAsync(user, dto.Password, false); if (result.Succeeded) { user.LastLoginAt = DateTime.UtcNow; await _userManager.UpdateAsync(user); _logger.LogInformation("User {Email} logged in successfully", dto.Email); return user; } _logger.LogWarning("Failed login attempt for {Email}", dto.Email); return null; } catch (Exception ex) { _logger.LogError(ex, "Error during login for {Email}", dto.Email); return null; } } public async Task<ApplicationUser?> GetByIdAsync(string id) { return await _userManager.FindByIdAsync(id); } public async Task<ApplicationUser?> GetByEmailAsync(string email) { return await _userManager.FindByEmailAsync(email); } public async Task<bool> UpdateAsync(string id, ApplicationUser user) { try { var existingUser = await _userManager.FindByIdAsync(id); if (existingUser == null) { return false; } existingUser.FirstName = user.FirstName; existingUser.LastName = user.LastName; existingUser.PhoneNumber = user.PhoneNumber; var result = await _userManager.UpdateAsync(existingUser); return result.Succeeded; } catch (Exception ex) { _logger.LogError(ex, "Error updating user {Id}", id); return false; } } public async Task<bool> DeleteAsync(string id) { try { var user = await _userManager.FindByIdAsync(id); if (user == null) { return false; } user.IsActive = false; var result = await _userManager.UpdateAsync(user); return result.Succeeded; } catch (Exception ex) { _logger.LogError(ex, "Error deleting user {Id}", id); return false; } } public async Task<IEnumerable<ApplicationUser>> GetAllAsync() { return await _userManager.Users.Where(u => u.IsActive).ToListAsync(); } }`, 'Services/IProductService.cs': `using \${projectName}.DTOs; using \${projectName}.Models; namespace \${projectName}.Services; public interface IProductService { Task<ProductResponseDto?> CreateAsync(ProductCreateDto dto); Task<ProductResponseDto?> GetByIdAsync(int id); Task<IEnumerable<ProductResponseDto>> GetAllAsync(); Task<IEnumerable<ProductResponseDto>> GetByCategoryAsync(int categoryId); Task<ProductResponseDto?> UpdateAsync(int id, ProductUpdateDto dto); Task<bool> DeleteAsync(int id); Task<bool> UpdateStockAsync(int id, int quantity); }`, 'Services/ProductService.cs': `using AutoMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Distributed; using Newtonsoft.Json; using \${projectName}.Data; using \${projectName}.DTOs; using \${projectName}.Models; namespace \${projectName}.Services; public class ProductService : IProductService { private readonly ApplicationDbContext _context; private readonly IMapper _mapper; private readonly IDistributedCache _cache; private readonly ILogger<ProductService> _logger; private const int CacheExpirationMinutes = 30; public ProductService( ApplicationDbContext context, IMapper mapper, IDistributedCache cache, ILogger<ProductService> logger) { _context = context; _mapper = mapper; _cache = cache; _logger = logger; } public async Task<ProductResponseDto?> CreateAsync(ProductCreateDto dto) { try { var product = _mapper.Map<Product>(dto); product.CreatedAt = DateTime.UtcNow; _context.Products.Add(product); await _context.SaveChangesAsync(); // Clear cache await _cache.RemoveAsync("products_all"); _logger.LogInformation("Product {ProductName} created with ID {ProductId}", product.Name, product.Id); return await GetByIdAsync(product.Id); } catch (Exception ex) { _logger.LogError(ex, "Error creating product"); return null; } } public async Task<ProductResponseDto?> GetByIdAsync(int id) { try { var cacheKey = $"product_{id}"; var cachedProduct = await _cache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedProduct)) { return JsonConvert.DeserializeObject<ProductResponseDto>(cachedProduct); } var product = await _context.Products .Include(p => p.Category) .FirstOrDefaultAsync(p => p.Id == id && p.IsActive); if (product == null) { return null; } var productDto = _mapper.Map<ProductResponseDto>(product); productDto.CategoryName = product.Category?.Name; // Cache the result var cacheOptions = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheExpirationMinutes) }; await _cache.SetStringAsync(cacheKey, JsonConvert.SerializeObject(productDto), cacheOptions); return productDto; } catch (Exception ex) { _logger.LogError(ex, "Error getting product {ProductId}", id); return null; } } public async Task<IEnumerable<ProductResponseDto>> GetAllAsync() { try { const string cacheKey = "products_all"; var cachedProducts = await _cache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedProducts)) { return JsonConvert.DeserializeObject<IEnumerable<ProductResponseDto>>(cachedProducts) ?? new List<ProductResponseDto>(); } var products = await _context.Products .Include(p => p.Category) .Where(p => p.IsActive) .OrderBy(p => p.Name) .ToListAsync(); var productDtos = products.Select(p => { var dto = _mapper.Map<ProductResponseDto>(p); dto.CategoryName = p.Category?.Name; return dto; }).ToList(); // Cache the result var cacheOptions = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheExpirationMinutes) }; await _cache.SetStringAsync(cacheKey, JsonConvert.SerializeObject(productDtos), cacheOptions); return productDtos; } catch (Exception ex) { _logger.LogError(ex, "Error getting all products"); return new List<ProductResponseDto>(); } } public async Task<IEnumerable<ProductResponseDto>> GetByCategoryAsync(int categoryId) { try { var products = await _context.Products .Include(p => p.Category) .Where(p => p.CategoryId == categoryId && p.IsActive) .OrderBy(p => p.Name) .ToListAsync(); return products.Select(p => { var dto = _mapper.Map<ProductResponseDto>(p); dto.CategoryName = p.Category?.Name; return dto; }).ToList(); } catch (Exception ex) { _logger.LogError(ex, "Error getting products for category {CategoryId}", categoryId); return new List<ProductResponseDto>(); } } public async Task<ProductResponseDto?> UpdateAsync(int id, ProductUpdateDto dto) { try { var product = await _context.Products.FindAsync(id); if (product == null || !product.IsActive) { return null; } _mapper.Map(dto, product); product.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); // Clear cache await _cache.RemoveAsync($"product_{id}"); await _cache.RemoveAsync("products_all"); _logger.LogInformation("Product {ProductId} updated", id); return await GetByIdAsync(id); } catch (Exception ex) { _logger.LogError(ex, "Error updating product {ProductId}", id); return null; } } public async Task<bool> DeleteAsync(int id) { try { var product = await _context.Products.FindAsync(id); if (product == null) { return false; } product.IsActive = false; product.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); // Clear cache await _cache.RemoveAsync($"product_{id}"); await _cache.RemoveAsync("products_all"); _logger.LogInformation("Product {ProductId} deleted", id); return true; } catch (Exception ex) { _logger.LogError(ex, "Error deleting product {ProductId}", id); return false; } } public async Task<bool> UpdateStockAsync(int id, int quantity) { try { var product = await _context.Products.FindAsync(id); if (product == null || !product.IsActive) { return false; } product.Stock = quantity; product.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); // Clear cache await _cache.RemoveAsync($"product_{id}"); await _cache.RemoveAsync("products_all"); _logger.LogInformation("Product {ProductId} stock updated to {Quantity}", id, quantity); return true; } catch (Exception ex) { _logger.LogError(ex, "Error updating stock for product {ProductId}", id); return false; } } }`, 'Services/ITokenService.cs': `using \${projectName}.Models; namespace \${projectName}.Services; public interface ITokenService { string GenerateJwtToken(ApplicationUser user); string GenerateRefreshToken(); bool ValidateJwtToken(string token); }`, 'Services/TokenService.cs': `using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using \${projectName}.Models; namespace \${projectName}.Services; public class TokenService : ITokenService { private readonly IConfiguration _configuration; private readonly ILogger<TokenService> _logger; public TokenService(IConfiguration configuration, ILogger<TokenService> logger) { _configuration = configuration; _logger = logger; } public string GenerateJwtToken(ApplicationUser user) { var jwtSettings = _configuration.GetSection("JwtSettings"); var secretKey = Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]!); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.Name, user.UserName!), new Claim(ClaimTypes.Email, user.Email!), new Claim("firstName", user.FirstName ?? ""), new Claim("lastName", user.LastName ?? ""), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) }; var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(jwtSettings["ExpirationInMinutes"])), Issuer = jwtSettings["Issuer"], Audience = jwtSettings["Audience"], SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha256Signature) }; var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); _logger.LogInformation("JWT token generated for user {UserId}", user.Id); return tokenHandler.WriteToken(token); } public string GenerateRefreshToken() { var randomNumber = new byte[32]; using var rng = RandomNumberGenerator.Create(); rng.GetBytes(randomNumber); return Convert.ToBase64String(randomNumber); } public bool ValidateJwtToken(string token) { try { var jwtSettings = _configuration.GetSection("JwtSettings"); var secretKey = Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]!); var tokenHandler = new JwtSecurityTokenHandler(); var validationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings["Issuer"], ValidAudience = jwtSettings["Audience"], IssuerSigningKey = new SymmetricSecurityKey(secretKey), ClockSkew = TimeSpan.Zero }; tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken); return true; } catch (Exception ex) { _logger.LogWarning(ex, "JWT token validation failed"); return false; } } }`, // Controllers 'Controllers/AuthController.cs': `using Microsoft.AspNetCore.Mvc; using \${projectName}.DTOs; using \${projectName}.Services; namespace \${projectName}.Controllers; /// <summary> /// Authentication controller for user registration and login /// </summary> [ApiController] [Route("api/[controller]")] [Produces("application/json")] public class AuthController : ControllerBase { private readonly IUserService _userService; private readonly ITokenService _tokenService; private readonly ILogger<AuthController> _logger; public AuthController( IUserService userService, ITokenService tokenService, ILogger<AuthController> logger) { _userService = userService; _tokenService = tokenService; _logger = logger; } /// <summary> /// Register a new user /// </summary> /// <param name="dto">User registration data</param> /// <returns>User registration result</returns> [HttpPost("register")] public async Task<IActionResult> Register([FromBody] UserRegisterDto dto) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var user = await _userService.RegisterAsync(dto); if (user == null) { return BadRequest(new { message = "User registration failed" }); } var token = _tokenService.GenerateJwtToken(user); return Ok(new { message = "User registered successfully", user = new { id = user.Id, email = user.Email, firstName = user.FirstName, lastName = user.LastName }, token }); } /// <summary> /// Authenticate user and return JWT token /// </summary> /// <param name="dto">User login credentials</param> /// <returns>Authentication result with JWT token</returns> [HttpPost("login")] public async Task<IActionResult> Login([FromBody] UserLoginDto dto) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var user = await _userService.LoginAsync(dto); if (user == null) { return Unauthorized(new { message = "Invalid credentials" }); } var token = _tokenService.GenerateJwtToken(user); return Ok(new { message = "Login successful", user = new { id = user.Id, email = user.Email, firstName = user.FirstName, lastName = user.LastName, lastLoginAt = user.LastLoginAt }, token }); } /// <summary> /// Validate JWT token /// </summary> /// <param name="token">JWT token to validate</param> /// <returns>Token validation result</returns> [HttpPost("validate")] public IActionResult ValidateToken([FromBody] string token) { var isValid = _tokenService.ValidateJwtToken(token); return Ok(new { isValid }); } }`, 'Controllers/ProductsController.cs': `using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using \${projectName}.DTOs; using \${projectName}.Services; namespace \${projectName}.Controllers; /// <summary> /// Products management controller /// </summary> [ApiController] [Route("api/[controller]")] [Produces("application/json")] [Authorize] public class ProductsController : ControllerBase { private readonly IProductService _productService; private readonly ILogger<ProductsController> _logger; public ProductsController( IProductService productService, ILogger<ProductsController> logger) { _productService = productService; _logger = logger; } /// <summary> /// Get all products /// </summary> /// <returns>List of products</returns> [HttpGet] [AllowAnonymous] public async Task<IActionResult> GetAll() { var products = await _productService.GetAllAsync(); return Ok(new { data = products, count = products.Count() }); } /// <summary> /// Get product by ID /// </summary> /// <param name="id">Product ID</param> /// <returns>Product details</returns> [HttpGet("{id}")] [AllowAnonymous] public async Task<IActionResult> GetById(int id) { var product = await _productService.GetByIdAsync(id); if (product == null) { return NotFound(new { message = "Product not found" }); } return Ok(new { data = product }); } /// <summary> /// Get products by category /// </summary> /// <param name="categoryId">Category ID</param> /// <returns>List of products in the category</returns> [HttpGet("category/{categoryId}")] [AllowAnonymous] public async Task<IActionResult> GetByCategory(int categoryId) { var products = await _productService.GetByCategoryAsync(categoryId); return Ok(new { data = products, count = products.Count() }); } /// <summary> /// Create a new product /// </summary> /// <param name="dto">Product creation data</param> /// <returns>Created product</returns> [HttpPost] public async Task<IActionResult> Create([FromBody] ProductCreateDto dto) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var product = await _productService.CreateAsync(dto); if (product == null) { return BadRequest(new { message = "Product creation failed" }); } return CreatedAtAction(nameof(GetById), new { id = product.Id }, new { data = product }); } /// <summary> /// Update an existing product /// </summary> /// <param name="id">Product ID</param> /// <param name="dto">Product update data</param> /// <returns>Updated product</returns> [HttpPut("{id}")] public async Task<IActionResult> Update(int id, [FromBody] ProductUpdateDto dto) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var product = await _productService.UpdateAsync(id, dto); if (product == null) { return NotFound(new { message = "Product not found" }); } return Ok(new { data = product, message = "Product updated successfully" }); } /// <summary> /// Delete a product /// </summary> /// <param name="id">Product ID</param> /// <returns>Deletion result</returns> [HttpDelete("{id}")] public async Task<IActionResult> Delete(int id) { var result = await _productService.DeleteAsync(id); if (!result) { return NotFound(new { message = "Product not found" }); } return Ok(new { message = "Product deleted successfully" }); } /// <summary> /// Update product stock /// </summary> /// <param name="id">Product ID</param> /// <param name="quantity">New stock quantity</param> /// <returns>Stock update result</returns> [HttpPatch("{id}/stock")] public async Task<IActionResult> UpdateStock(int id, [FromBody] int quantity) { if (quantity < 0) { return BadRequest(new { message = "Stock quantity cannot be negative" }); } var result = await _productService.UpdateStockAsync(id, quantity); if (!result) { return NotFound(new { message = "Product not found" }); } return Ok(new { message = "Stock updated successfully" }); } }`, // Middleware 'Middleware/ExceptionHandlingMiddleware.cs': `using System.Net; using System.Text.Json; namespace \${projectName}.Middleware; public class ExceptionHandlingMiddleware { private readonly RequestDelegate _next; private readonly ILogger<ExceptionHandlingMiddleware> _logger; public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception ex) { _logger.LogError(ex, "An unhandled exception occurred"); await HandleExceptionAsync(context, ex); } } private static async Task HandleExceptionAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; var response = new { error = new { message = "An error occurred while processing your request", details = exception.Message, timestamp = DateTime.UtcNow } }; switch (exception) { case ArgumentException: context.Response.StatusCode = (int)HttpStatusCode.BadRequest; break; case UnauthorizedAccessException: context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; break; case KeyNotFoundException: context.Response.StatusCode = (int)HttpStatusCode.NotFound; break; default: context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; break; } var jsonResponse = JsonSerializer.Serialize(response, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); await context.Response.WriteAsync(jsonResponse); } }`, 'Middleware/RequestLoggingMiddleware.cs': `namespace \${projectName}.Middleware; public class RequestLoggingMiddleware { private readonly RequestDelegate _next; private readonly ILogger<RequestLoggingMiddleware> _logger; public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { var startTime = DateTime.UtcNow; _logger.LogInformation("Request {Method} {Path} started at {StartTime}", context.Request.Method, context.Request.Path, startTime); await _next(context); var endTime = DateTime.UtcNow; var duration = endTime - startTime; _logger.LogInformation("Request {Method} {Path} completed with {StatusCode} in {Duration}ms", context.Request.Method, context.Request.Path, context.Response.StatusCode, duration.TotalMilliseconds); } }`, // Extensions 'Extensions/ServiceCollectionExtensions.cs': `using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System.Text; using \${projectName}.Data; using \${projectName}.Models; using \${projectName}.Services; namespace \${projectName}.Extensions; public static class ServiceCollectionExtensions { public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration) { // Database services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"))); // Identity services.AddIdentity<ApplicationUser, IdentityRole>(options => { options.Password.RequireDigit = true; options.Password.RequireLowercase = true; options.Password.RequireUppercase = true; options.Password.RequireNonAlphanumeric = false; options.Password.RequiredLength = 6; options.User.RequireUniqueEmail = true; }) .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); // JWT Authentication var jwtSettings = configuration.GetSection("JwtSettings"); 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(jwtSettings["SecretKey"]!)) }; }); // Business Services services.AddScoped<IUserService, UserService>(); services.AddScoped<IProductService, ProductService>(); services.AddScoped<ITokenService, TokenService>(); // AutoMapper services.AddAutoMapper(typeof(Program)); return services; } }`, // Configuration files 'appsettings.json': `{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Microsoft.EntityFrameworkCore": "Information" } }, "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\\\mssqllocaldb;Database=\${projectName}Db;Trusted_Connection=true;MultipleActiveResultSets=true", "Redis": "localhost:6379" }, "JwtSettings": { "SecretKey": "YourSuperSecretKeyThatIsAtLeast32CharactersLong!", "Issuer": "\${projectName}-api", "Audience": "\${projectName}-client", "ExpirationInMinutes": 60 } }`, 'appsettings.Development.json': `{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Microsoft.EntityFrameworkCore": "Information" } }, "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\\\mssqllocaldb;Database=\${projectName}DevDb;Trusted_Connection=true;MultipleActiveResultSets=true", "Redis": "localhost:6379" } }`, // AutoMapper Profile 'Profiles/MappingProfile.cs': `using AutoMapper; using \${projectName}.DTOs; using \${projectName}.Models; namespace \${projectName}.Profiles; public class MappingProfile : Profile { public MappingProfile() { // Product mappings CreateMap<ProductCreateDto, Product>(); CreateMap<Product