diff --git a/Project.Application/DTOs/Auth/AuthCheckDto.cs b/Project.Application/DTOs/Auth/AuthCheckDto.cs new file mode 100644 index 0000000..9628b13 --- /dev/null +++ b/Project.Application/DTOs/Auth/AuthCheckDto.cs @@ -0,0 +1,4 @@ +namespace Project.Application.DTOs.Auth +{ + public record AuthCheckDto(bool IsAuthenticated); +} diff --git a/Project.Application/DTOs/Auth/LoginDto.cs b/Project.Application/DTOs/Auth/LoginDto.cs new file mode 100644 index 0000000..bfd226f --- /dev/null +++ b/Project.Application/DTOs/Auth/LoginDto.cs @@ -0,0 +1,4 @@ +namespace Project.Application.DTOs.Auth +{ + public record LoginDto(string Username, string Password); +} diff --git a/Project.Application/DTOs/Incoming/CreatingProductDto.cs b/Project.Application/DTOs/Incoming/CreatingProductDto.cs index ea72177..97ca203 100644 --- a/Project.Application/DTOs/Incoming/CreatingProductDto.cs +++ b/Project.Application/DTOs/Incoming/CreatingProductDto.cs @@ -1,4 +1,5 @@ -using Project.Domain.Entities; +using Project.Application.DTOs.Outgoing; +using Project.Domain.Entities; namespace Project.Application.DTOs.Incoming { @@ -6,6 +7,6 @@ namespace Project.Application.DTOs.Incoming { public string Name { get; set; } public decimal Price { get; set; } - public CreatingCategoryDto? Category { get; set; } + public int CategoryId { get; set; } } } diff --git a/Project.Application/DTOs/Incoming/CreatingRoleDto.cs b/Project.Application/DTOs/Incoming/CreatingRoleDto.cs new file mode 100644 index 0000000..74d1104 --- /dev/null +++ b/Project.Application/DTOs/Incoming/CreatingRoleDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Project.Application.DTOs.Incoming +{ + public class CreatingRoleDto + { + public string Name { get; set; } + } +} diff --git a/Project.Application/DTOs/Incoming/CreatingUserDto.cs b/Project.Application/DTOs/Incoming/CreatingUserDto.cs new file mode 100644 index 0000000..4bed5a1 --- /dev/null +++ b/Project.Application/DTOs/Incoming/CreatingUserDto.cs @@ -0,0 +1,15 @@ +namespace Project.Application.DTOs.Incoming +{ + public class CreatingUserDto + { + public string UserName { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + + public string Password { get; init; } + + public int RoleId { get; set; } + } +} diff --git a/Project.Application/DTOs/Incoming/UpdatingProductDto.cs b/Project.Application/DTOs/Incoming/UpdatingProductDto.cs index f0e3c66..942afb9 100644 --- a/Project.Application/DTOs/Incoming/UpdatingProductDto.cs +++ b/Project.Application/DTOs/Incoming/UpdatingProductDto.cs @@ -11,6 +11,6 @@ namespace Project.Application.DTOs.Incoming public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } - public CreatingCategoryDto? Category { get; set; } + public int CategoryId { get; set; } } } diff --git a/Project.Application/DTOs/Incoming/UpdatingRoleDto.cs b/Project.Application/DTOs/Incoming/UpdatingRoleDto.cs new file mode 100644 index 0000000..74d7e2a --- /dev/null +++ b/Project.Application/DTOs/Incoming/UpdatingRoleDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Project.Application.DTOs.Incoming +{ + public class UpdatingRoleDto + { + public int Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/Project.Application/DTOs/Incoming/UpdatingUserDto.cs b/Project.Application/DTOs/Incoming/UpdatingUserDto.cs new file mode 100644 index 0000000..98045e7 --- /dev/null +++ b/Project.Application/DTOs/Incoming/UpdatingUserDto.cs @@ -0,0 +1,17 @@ +namespace Project.Application.DTOs.Incoming +{ + public class UpdatingUserDto + { + public int Id { get; set; } + + public string UserName { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + + public string Password { get; init; } + + public int RoleId { get; set; } + } +} diff --git a/Project.Application/DTOs/Outgoing/ReadingRoleDto.cs b/Project.Application/DTOs/Outgoing/ReadingRoleDto.cs new file mode 100644 index 0000000..c581fa6 --- /dev/null +++ b/Project.Application/DTOs/Outgoing/ReadingRoleDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Project.Application.DTOs.Outgoing +{ + public class ReadingRoleDto + { + public int Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/Project.Application/DTOs/Outgoing/ReadingUserDto.cs b/Project.Application/DTOs/Outgoing/ReadingUserDto.cs new file mode 100644 index 0000000..fd270fe --- /dev/null +++ b/Project.Application/DTOs/Outgoing/ReadingUserDto.cs @@ -0,0 +1,14 @@ +namespace Project.Application.DTOs.Outgoing +{ + public class ReadingUserDto + { + public int Id { get; set; } + + public string UserName { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + public ReadingRoleDto? Role { get; set; } + } +} diff --git a/Project.Application/Interfaces/IAuthService.cs b/Project.Application/Interfaces/IAuthService.cs new file mode 100644 index 0000000..2c83f39 --- /dev/null +++ b/Project.Application/Interfaces/IAuthService.cs @@ -0,0 +1,10 @@ +using Project.Domain.Entities; + +namespace Project.Application.Interfaces +{ + public interface IAuthService + { + // AUTHENTICATE + Task ValidateAsync(string username, string password); + } +} diff --git a/Project.Application/Interfaces/IRoleService.cs b/Project.Application/Interfaces/IRoleService.cs new file mode 100644 index 0000000..f5775bb --- /dev/null +++ b/Project.Application/Interfaces/IRoleService.cs @@ -0,0 +1,27 @@ +using Project.Application.DTOs.Incoming; +using Project.Application.DTOs.Outgoing; +using Project.Domain.Entities; + +namespace Project.Application.Interfaces +{ + public interface IRoleService + { + // CREATE + Task AddRoleAsync(CreatingRoleDto creatingRoleDto); + + // READ ALL + Task> GetAllAsync(); + + // READ BY ID + Task GetByIdAsync(int id); + + // READ BY NAME + Task GetByNameAsync(string name); + + // UPDATE + Task UpdateRoleAsync(UpdatingRoleDto updatedRoleDto); + + // DELETE + Task DeleteRoleAsync(int id); + } +} diff --git a/Project.Application/Interfaces/IUserService.cs b/Project.Application/Interfaces/IUserService.cs new file mode 100644 index 0000000..f29e533 --- /dev/null +++ b/Project.Application/Interfaces/IUserService.cs @@ -0,0 +1,30 @@ +using Project.Application.DTOs.Incoming; +using Project.Application.DTOs.Outgoing; +using Project.Domain.Entities; + +namespace Project.Application.Interfaces +{ + public interface IUserService + { + // CREATE + Task AddUserAsync(CreatingUserDto creatingUserDto); + + // READ ALL + Task> GetUsersAsync(); + + // READ BY ID + Task GetByIdAsync(int id); + + // READ BY USERNAME + Task GetByUsernameAsync(string username); + + // UPDATE + Task UpdateUserAsync(UpdatingUserDto updatingUserDto); + + // UPDATE USER ROLE -- die Rolle eines Users aktualisieren + Task UpdateUserRoleAsync(int userId, int roleId); + + // DELETE + Task DeleteUserAsync(int id); + } +} diff --git a/Project.Application/MappingProfiles/BasicDtoMappingProfile.cs b/Project.Application/MappingProfiles/BasicDtoMappingProfile.cs index 06093a3..97c6297 100644 --- a/Project.Application/MappingProfiles/BasicDtoMappingProfile.cs +++ b/Project.Application/MappingProfiles/BasicDtoMappingProfile.cs @@ -9,13 +9,25 @@ namespace Project.Application.MappingProfiles { public BasicDtoMappingProfile() { + // CATEGORY CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + // PRODUCT CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + + // ROLE + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + // USER + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } } } diff --git a/Project.Application/Project.Application.csproj b/Project.Application/Project.Application.csproj index 39f7fcb..9ff9ed5 100644 --- a/Project.Application/Project.Application.csproj +++ b/Project.Application/Project.Application.csproj @@ -7,6 +7,7 @@ + diff --git a/Project.Application/Services/AuthService.cs b/Project.Application/Services/AuthService.cs new file mode 100644 index 0000000..04dc6f5 --- /dev/null +++ b/Project.Application/Services/AuthService.cs @@ -0,0 +1,26 @@ +using Project.Application.Interfaces; +using Project.Domain.Entities; +using Project.Infrastructure.Interfaces; + +namespace Project.Application.Services +{ + public class AuthService : IAuthService + { + // FIELDS FOR CTOR + private IUserRepository _userRepository; + + // CTOR + public AuthService(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + // AUTHENTICATE + public async Task ValidateAsync(string username, string password) + { + var user = await _userRepository.GetByUsernameAsync(username); + + return user?.Password == password; + } + } +} diff --git a/Project.Application/Services/ProductService.cs b/Project.Application/Services/ProductService.cs index 5d344d9..5e695bc 100644 --- a/Project.Application/Services/ProductService.cs +++ b/Project.Application/Services/ProductService.cs @@ -56,8 +56,8 @@ namespace Project.Application.Services public async Task UpdateProductAsync(UpdatingProductDto updatingProductDto) { var product = _mapper.Map(updatingProductDto); - bool idUpdated = await _productRepository.UpdateAsync(product); - return idUpdated; + bool isUpdated = await _productRepository.UpdateAsync(product); + return isUpdated; } // DELETE diff --git a/Project.Application/Services/RoleService.cs b/Project.Application/Services/RoleService.cs new file mode 100644 index 0000000..ab945e8 --- /dev/null +++ b/Project.Application/Services/RoleService.cs @@ -0,0 +1,75 @@ +using AutoMapper; +using Project.Application.DTOs.Incoming; +using Project.Application.DTOs.Outgoing; +using Project.Application.Interfaces; +using Project.Domain.Entities; +using Project.Infrastructure.Interfaces; + +namespace Project.Application.Services +{ + public class RoleService : IRoleService + { + // FIELDS FOR CTOR + private readonly IRoleRepository _roleRepository; + private readonly IMapper _mapper; + + // CTOR + public RoleService(IRoleRepository roleRepository, IMapper mapper) + { + _roleRepository = roleRepository; + _mapper = mapper; + } + + // CREATE + public async Task AddRoleAsync(CreatingRoleDto creatingRoleDto) + { + var role = _mapper.Map(creatingRoleDto); + var created = await _roleRepository.AddAsync(role); + return created; + } + + // READ ALL + public async Task> GetAllAsync() + { + var roles = await _roleRepository.GetAllAsync(); + var readDto = _mapper.Map>(roles); + return readDto; + } + + // READ BY ID + public async Task GetByIdAsync(int id) + { + var role = await _roleRepository.GetByIdAsync(id); + var readDto = _mapper.Map(role); + return readDto; + } + + // READ BY NAME + public async Task GetByNameAsync(string name) + { + var role = await _roleRepository.GetByNameAsync(name); + var readDto = _mapper.Map(role); + return readDto; + } + + // UPDATE + public async Task UpdateRoleAsync(UpdatingRoleDto updatingRoleDto) + { + var role = _mapper.Map(updatingRoleDto); + bool isUpdated = await _roleRepository.UpdateAsync(role); + return isUpdated; + } + + // DELETE + public async Task DeleteRoleAsync(int id) + { + Role? role = await _roleRepository.GetByIdAsync(id); + + if (role is null) + return false; + + bool isDeleted = await _roleRepository.DeleteAsync(role); + return isDeleted; + } + } +} diff --git a/Project.Application/Services/UserService.cs b/Project.Application/Services/UserService.cs new file mode 100644 index 0000000..b106440 --- /dev/null +++ b/Project.Application/Services/UserService.cs @@ -0,0 +1,104 @@ +using AutoMapper; +using Project.Application.DTOs.Incoming; +using Project.Application.DTOs.Outgoing; +using Project.Application.Interfaces; +using Project.Domain.Entities; +using Project.Infrastructure.Interfaces; + +namespace Project.Application.Services +{ + public class UserService : IUserService + { + // FIELDS FOR CTOR + private readonly IUserRepository _userRepository; + private readonly IRoleRepository _roleRepository; + private readonly IMapper _mapper; + + // CTOR + public UserService(IUserRepository userRepository, IRoleRepository roleRepository, IMapper mapper) + { + _userRepository = userRepository; + _roleRepository = roleRepository; + _mapper = mapper; + } + + // CREATE + public async Task AddUserAsync(CreatingUserDto creatingUserDto) + { + // validating role + var role = await _roleRepository.GetByIdAsync(creatingUserDto.RoleId); + if (role == null) + { + throw new ArgumentException("Role not found"); + } + + // mapping dto to entity + var user = _mapper.Map(creatingUserDto); + var created = await _userRepository.AddAsync(user); + return created; + } + + // READ ALL + public async Task> GetUsersAsync() + { + var users = await _userRepository.GetAllAsync(); + var readDto = _mapper.Map>(users); + return readDto; + } + + // READ BY ID + public async Task GetByIdAsync(int id) + { + var user = await _userRepository.GetByIdAsync(id); + var readDto = _mapper.Map(user); + return readDto; + } + + // READ BY USERNAME + public async Task GetByUsernameAsync(string username) + { + var user = await _userRepository.GetByUsernameAsync(username); + var readDto = _mapper.Map(user); + return readDto; + } + + // UPDATE + public async Task UpdateUserAsync(UpdatingUserDto updatingUserDto) + { + var user = _mapper.Map(updatingUserDto); + bool isUpdated = await _userRepository.UpdateAsync(user); + return isUpdated; + } + + // UPDATE USER ROLE -- die Rolle eines Users aktualisieren + public async Task UpdateUserRoleAsync(int userId, int roleId) + { + var user = await _userRepository.GetByIdAsync(userId); + if (user == null) + { + throw new ArgumentException("User not found"); + } + + var role = await _roleRepository.GetByIdAsync(roleId); + if (role == null) + { + throw new ArgumentException("Role not found"); + } + + user.RoleId = roleId; + await _userRepository.SaveAsync(); + } + + // DELETE + public async Task DeleteUserAsync(int id) + { + User? user = await _userRepository.GetByIdAsync(id); + + if (user is null) + return false; + + bool isDeleted = await _userRepository.DeleteAsync(user); + return isDeleted; + } + } +} diff --git a/Project.Domain/Entities/Category.cs b/Project.Domain/Entities/Category.cs index 4fbd789..7909a87 100644 --- a/Project.Domain/Entities/Category.cs +++ b/Project.Domain/Entities/Category.cs @@ -10,9 +10,11 @@ namespace Project.Domain.Entities [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Column("ID")] public int Id { get; set; } = 0; + [Required] [Column("CATEGORY_NAME")] public string Name { get; set; } + [Required] [Column("CREATION_DATE", TypeName = "datetime")] public DateTime CreationDate { get; set; } = DateTime.Now; diff --git a/Project.Domain/Entities/CategoryRole.cs b/Project.Domain/Entities/CategoryRole.cs index 28bcaa6..71ca621 100644 --- a/Project.Domain/Entities/CategoryRole.cs +++ b/Project.Domain/Entities/CategoryRole.cs @@ -10,12 +10,16 @@ namespace Project.Domain.Entities [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Column("ID")] public int Id { get; set; } + public int CategoryId { get; set; } + public int RoleId { get; set; } + [ForeignKey("CategoryId")] [Required] [Column("PRODUCT_CATEGORY")] public Category? Category { get; set; } + [ForeignKey("RoleId")] public Role? Role { get; set; } } diff --git a/Project.Domain/Entities/Product.cs b/Project.Domain/Entities/Product.cs index 05afaf1..011eed8 100644 --- a/Project.Domain/Entities/Product.cs +++ b/Project.Domain/Entities/Product.cs @@ -10,13 +10,17 @@ namespace Project.Domain.Entities [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Column("ID")] public int Id { get; set; } = 0; + [Required] [Column("PRODUCT_NAME")] public string Name { get; set; } + [Required] [Column("PRICE", TypeName = "decimal(18,2)")] public decimal Price { get; set; } + public int CategoryId { get; set; } + [ForeignKey("CategoryId")] [Column("PRODUCT_CATEGORY")] public Category? Category { get; set; } diff --git a/Project.Domain/Entities/Role.cs b/Project.Domain/Entities/Role.cs index 7df6734..c7bfeb8 100644 --- a/Project.Domain/Entities/Role.cs +++ b/Project.Domain/Entities/Role.cs @@ -10,11 +10,13 @@ namespace Project.Domain.Entities [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Column("ID")] public int Id { get; set; } + [Required] [Column("ROLE")] public string Name { get; set; } + [Required] [Column("CREATION_DATE", TypeName = "datetime")] - public DateTime CreationDate { get; set; } + public DateTime CreationDate { get; set; } = DateTime.Now; } } diff --git a/Project.Domain/Entities/User.cs b/Project.Domain/Entities/User.cs index 9cd8ad9..bb35dbb 100644 --- a/Project.Domain/Entities/User.cs +++ b/Project.Domain/Entities/User.cs @@ -6,21 +6,29 @@ namespace Project.Domain.Entities [Table("USER", Schema = "dbo")] public class User { + [Column("ID")] [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - [Column("ID")] public int Id { get; set; } + [Required] [Column("USER_NAME")] public string UserName { get; set; } + [Required] [Column("FIRST_NAME")] public string FirstName { get; set; } + [Required] [Column("LAST_NAME")] public string LastName { get; set; } + [Required] + [Column("PASSWORD")] + public string Password { get; init; } + public int RoleId { get; set; } + [ForeignKey("RoleId")] public Role? Role { get; set; } } diff --git a/Project.Domain/Project.Domain.csproj b/Project.Domain/Project.Domain.csproj index f67dbdd..7a8cadb 100644 --- a/Project.Domain/Project.Domain.csproj +++ b/Project.Domain/Project.Domain.csproj @@ -7,6 +7,7 @@ + diff --git a/Project.Infrastructure/ApplicationDbContext.cs b/Project.Infrastructure/ApplicationDbContext.cs index 4836c30..ec143a4 100644 --- a/Project.Infrastructure/ApplicationDbContext.cs +++ b/Project.Infrastructure/ApplicationDbContext.cs @@ -20,7 +20,10 @@ namespace Project.Infrastructure { base.OnModelCreating(modelBuilder); - modelBuilder.Entity(); + modelBuilder.Entity() + .HasOne(u => u.Role) + .WithMany() + .HasForeignKey(u => u.RoleId); } } } diff --git a/Project.Infrastructure/Interfaces/IRoleRepository.cs b/Project.Infrastructure/Interfaces/IRoleRepository.cs new file mode 100644 index 0000000..e798944 --- /dev/null +++ b/Project.Infrastructure/Interfaces/IRoleRepository.cs @@ -0,0 +1,25 @@ +using Project.Domain.Entities; + +namespace Project.Infrastructure.Interfaces +{ + public interface IRoleRepository + { + // CREATE + Task AddAsync(Role role); + + // READ ALL + Task> GetAllAsync(); + + // READ BY ID + Task GetByIdAsync(int id); + + // READ BY NAME + Task GetByNameAsync(string name); + + // UPDATE + Task UpdateAsync(Role role); + + // DELETE + Task DeleteAsync(Role role); + } +} diff --git a/Project.Infrastructure/Interfaces/IUserRepository.cs b/Project.Infrastructure/Interfaces/IUserRepository.cs new file mode 100644 index 0000000..d0d50b3 --- /dev/null +++ b/Project.Infrastructure/Interfaces/IUserRepository.cs @@ -0,0 +1,28 @@ +using Project.Domain.Entities; + +namespace Project.Infrastructure.Interfaces +{ + public interface IUserRepository + { + // CREATE + Task AddAsync(User user); + + // READ ALL + Task> GetAllAsync(); + + // READ BY ID + Task GetByIdAsync(int id); + + // READ BY USERNAME + Task GetByUsernameAsync(string username); + + // UPDATE + Task UpdateAsync(User user); + + // DELETE + Task DeleteAsync(User user); + + // SAVE + Task SaveAsync(); + } +} diff --git a/Project.Infrastructure/Project.Infrastructure.csproj b/Project.Infrastructure/Project.Infrastructure.csproj index 259bc09..53f2a15 100644 --- a/Project.Infrastructure/Project.Infrastructure.csproj +++ b/Project.Infrastructure/Project.Infrastructure.csproj @@ -8,6 +8,7 @@ + diff --git a/Project.Infrastructure/Repositories/CategoryRepository.cs b/Project.Infrastructure/Repositories/CategoryRepository.cs index 5dd98c5..de5744b 100644 --- a/Project.Infrastructure/Repositories/CategoryRepository.cs +++ b/Project.Infrastructure/Repositories/CategoryRepository.cs @@ -9,13 +9,11 @@ namespace Project.Infrastructure.Repositories { // FIELDS FOR CTOR private readonly ApplicationDbContext _context; - private readonly IMapper _mapper; // CTOR - public CategoryRepository(ApplicationDbContext context, IMapper mapper) + public CategoryRepository(ApplicationDbContext context) { _context = context; - _mapper = mapper; } // CREATE diff --git a/Project.Infrastructure/Repositories/ProductRepository.cs b/Project.Infrastructure/Repositories/ProductRepository.cs index 9ed85dc..832171a 100644 --- a/Project.Infrastructure/Repositories/ProductRepository.cs +++ b/Project.Infrastructure/Repositories/ProductRepository.cs @@ -9,13 +9,11 @@ namespace Project.Infrastructure.Repositories { // FIELDS FOR CTOR private readonly ApplicationDbContext _context; - private readonly IMapper _mapper; // CTOR - public ProductRepository(ApplicationDbContext context, IMapper mapper) + public ProductRepository(ApplicationDbContext context) { _context = context; - _mapper = mapper; } // CREATE @@ -27,7 +25,6 @@ namespace Project.Infrastructure.Repositories } // READ ALL - [Authorize] public async Task> GetAllAsync() { return await _context.Products.Include(p => p.Category).ToListAsync(); diff --git a/Project.Infrastructure/Repositories/RoleRepository.cs b/Project.Infrastructure/Repositories/RoleRepository.cs new file mode 100644 index 0000000..e24c366 --- /dev/null +++ b/Project.Infrastructure/Repositories/RoleRepository.cs @@ -0,0 +1,61 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Project.Domain.Entities; +using Project.Infrastructure.Interfaces; + +namespace Project.Infrastructure.Repositories +{ + public class RoleRepository : IRoleRepository + { + // FIELDS FOR CTOR + private readonly ApplicationDbContext _context; + + // CTOR + public RoleRepository(ApplicationDbContext context) + { + _context = context; + } + + // CREATE + public async Task AddAsync(Role role) + { + await _context.Roles.AddAsync(role); + await _context.SaveChangesAsync(); + return role; + } + + // READ ALL + public async Task> GetAllAsync() + { + return await _context.Roles.ToListAsync(); + } + + // READ BY ID + public async Task GetByIdAsync(int id) + { + return await _context.Roles.FindAsync(id); + } + + // READ BY NAME + public async Task GetByNameAsync(string name) + { + return await _context.Roles.FirstOrDefaultAsync(n => n.Name == name); + } + + // UPDATE + public async Task UpdateAsync(Role role) + { + _context.Entry(role).State = EntityState.Modified; + var results = await _context.SaveChangesAsync(); + return results > 0; + } + + // DELETE + public async Task DeleteAsync(Role role) + { + _context.Roles.Remove(role); + var result = await _context.SaveChangesAsync(); + return result > 0; + } + } +} diff --git a/Project.Infrastructure/Repositories/UserRepository.cs b/Project.Infrastructure/Repositories/UserRepository.cs new file mode 100644 index 0000000..3282aab --- /dev/null +++ b/Project.Infrastructure/Repositories/UserRepository.cs @@ -0,0 +1,68 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Project.Domain.Entities; +using Project.Infrastructure.Interfaces; + +namespace Project.Infrastructure.Repositories +{ + public class UserRepository : IUserRepository + { + // FIELDS FOR CTOR + private readonly ApplicationDbContext _context; + + // CTOR + public UserRepository(ApplicationDbContext context) + { + _context = context; + } + + // CREATE + public async Task AddAsync(User user) + { + await _context.Users.AddAsync(user); + await _context.SaveChangesAsync(); + return user; + } + + // READ ALL + public async Task> GetAllAsync() + { + return await _context.Users.Include(u => u.Role).ToListAsync(); + } + + // READ BY ID + public async Task GetByIdAsync(int id) + { + return await _context.Users.FindAsync(id); + } + + // READ BY USERNAME + public async Task GetByUsernameAsync(string username) + { + return await _context.Users.FirstOrDefaultAsync(u => u.UserName == username); + } + + // UPDATE + public async Task UpdateAsync(User user) + { + _context.Entry(user).State = EntityState.Modified; + var results = await _context.SaveChangesAsync(); + return results > 0; + } + + // DELETE + public async Task DeleteAsync(User user) + { + _context.Users.Remove(user); + var result = await _context.SaveChangesAsync(); + return result > 0; + } + + // SAVE + public async Task SaveAsync() + { + var saved = await _context.SaveChangesAsync(); + return saved > 0 ? true : false; + } + } +} diff --git a/Project.Web/Controllers/AuthController.cs b/Project.Web/Controllers/AuthController.cs new file mode 100644 index 0000000..3ea71f9 --- /dev/null +++ b/Project.Web/Controllers/AuthController.cs @@ -0,0 +1,131 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Project.Application.DTOs.Auth; +using Project.Application.DTOs.Outgoing; +using Project.Application.Interfaces; +using System.Security.Claims; + +namespace Project.Web.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class AuthController : ControllerBase + { + // FIELDS FOR CTOR + private readonly IUserService _userService; + private readonly IAuthService _authService; + + // CTOR + public AuthController(IUserService userService, IAuthService authService) + { + _userService = userService; + _authService = authService; + } + + // LOGIN + [AllowAnonymous] + [HttpPost("login")] + public async Task Login([FromBody] LoginDto login) + { + var isValid = await _authService.ValidateAsync(login.Username, login.Password); + + if (!isValid) + { + return Unauthorized(); + } + + var user = await _userService.GetByUsernameAsync(login.Username); + if (user == null) + { + return Unauthorized(user); + } + + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), + new Claim(ClaimTypes.Name, user.UserName), + new Claim(ClaimTypes.Surname, user.LastName ?? ""), + new Claim(ClaimTypes.GivenName, user.FirstName ?? "") + }; + + var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); + + var authProperties = new AuthenticationProperties + { + IsPersistent = true, + AllowRefresh = true, + ExpiresUtc = DateTime.UtcNow.AddMinutes(60) + }; + + await HttpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(claimsIdentity), + authProperties + ); + + return Ok(); + } + + // LOGOUT + [HttpPost("logout")] + public async Task Logout() + { + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + return Ok(); + } + + + + + + + + + + + + + + + + //// LOGIN + //[HttpPost("login")] + //public async Task Login(LoginDto login) + //{ + // var user = await _authService.AuthenticateAsync(login.Username, login.Password); + + // if (user == null) + // { + // return Unauthorized(); + // } + + // var claims = new ClaimsIdentity(new[] + // { + // new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), + // new Claim(ClaimTypes.Name, user.UserName) + // }, CookieAuthenticationDefaults.AuthenticationScheme); + + // var authProperties = new AuthenticationProperties + // { + // IsPersistent = false, + // ExpiresUtc = DateTime.UtcNow.AddMinutes(10) + // }; + + // await HttpContext.SignInAsync( + // CookieAuthenticationDefaults.AuthenticationScheme, + // new ClaimsPrincipal(claims), + // authProperties); + + // return Ok("Login successful"); + //} + + //// LOGOUT + //public async Task Logout() + //{ + // await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + // return Ok("Logout successful"); + //} + } +} diff --git a/Project.Web/Controllers/CategoryController.cs b/Project.Web/Controllers/CategoryController.cs index fc41692..4319436 100644 --- a/Project.Web/Controllers/CategoryController.cs +++ b/Project.Web/Controllers/CategoryController.cs @@ -8,7 +8,6 @@ namespace Project.Web.Controllers [ApiController] public class CategoryController : ControllerBase { - // FIELDS FOR CTOR private readonly ICategoryService _categoryService; @@ -25,19 +24,31 @@ namespace Project.Web.Controllers [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task CreateCategory([FromBody] CreatingCategoryDto creatingCategoryDto) { - var result = await _categoryService.AddCategoryAsync(creatingCategoryDto); - - if (result != null) + if (!ModelState.IsValid) { - var id = result.Id; - var createdResource = new { Id = id }; - var actionName = nameof(GetCategoryById); - var routeValue = new { id = createdResource.Id }; - return CreatedAtAction(actionName, routeValue, createdResource); + return BadRequest(ModelState); } - else + + try { - return BadRequest("geht nix"); + var result = await _categoryService.AddCategoryAsync(creatingCategoryDto); + + if (result != null) + { + var id = result.Id; + var createdResource = new { Id = id }; + var actionName = nameof(GetCategoryById); + var routeValue = new { id = createdResource.Id }; + return CreatedAtAction(actionName, routeValue, createdResource); + } + else + { + return BadRequest("geht nix"); + } + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } } diff --git a/Project.Web/Controllers/ProductController.cs b/Project.Web/Controllers/ProductController.cs index 39f8779..2cddfc2 100644 --- a/Project.Web/Controllers/ProductController.cs +++ b/Project.Web/Controllers/ProductController.cs @@ -2,23 +2,22 @@ using Microsoft.AspNetCore.Mvc; using Project.Application.DTOs.Incoming; using Project.Application.Interfaces; -using Project.Application.Services; -using System.Security.Claims; namespace Project.Web.Controllers { [Route("api/[controller]")] [ApiController] + [Authorize(Roles = "Admin")] public class ProductController : ControllerBase { - // FIELDS FOR CTOR private readonly IProductService _productService; - + private readonly IUserService _userService; // CTOR - public ProductController(IProductService productService) + public ProductController(IProductService productService, IUserService userService) { _productService = productService; + _userService = userService; } // CREATE @@ -26,26 +25,48 @@ namespace Project.Web.Controllers [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task CreateProduct([FromBody] CreatingProductDto creatingProductDto) // with form body + //[AllowAnonymous] + public async Task CreateProduct([FromBody] CreatingProductDto creatingProductDto) { - var result = await _productService.AddProductAsync(creatingProductDto); - - if (result != null) + if (!ModelState.IsValid) { - var id = result.Id; - var createdResource = new { Id = id }; - var actionName = nameof(GetProductById); - var routeValue = new { id = createdResource.Id }; - return CreatedAtAction(actionName, routeValue, createdResource); + return BadRequest(ModelState); } - else + + try { - return BadRequest("geht nix"); + + var result = await _productService.AddProductAsync(creatingProductDto); + + //--------- + //var current_user = await GetUser(); + //if (current_user is null) + // return Unauthorized(); + + //_productService.getbyUsername(current_user.UserName); + //---------- + + if (result != null) + { + var id = result.Id; + var createdResource = new { Id = id }; + var actionName = nameof(GetProductById); + var routeValue = new { id = createdResource.Id }; + return CreatedAtAction(actionName, routeValue, createdResource); + } + else + { + return BadRequest("geht nix"); + } + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); } } // READ ALL - [Authorize(Roles ="Admin")] + //[Authorize(Roles = "Admin")] // Authorization [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetProducts() @@ -54,14 +75,6 @@ namespace Project.Web.Controllers return Ok(products); } - public async Task GetProducts() - { - var id = User.FindFirst(ClaimTypes.NameIdentifier); - - var products = await _productService.getproductbyuserid(id); - return Ok(products); - } - // READ BY ID [HttpGet("id/{id}", Name = "GetProductById")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -120,5 +133,17 @@ namespace Project.Web.Controllers await _productService.DeleteProductAsync(id); return Ok(); } + + //--------------- + //async Task GetUser() + //{ + // var id_st = this.User.FindFirstValue(ClaimTypes.NameIdentifier); + // if (int.TryParse(id_st, out int id)) + // return null; + + // var user = await _userService.GetByIdAsync(id); + // return user; + //} + //-------------- } } diff --git a/Project.Web/Controllers/RoleController.cs b/Project.Web/Controllers/RoleController.cs new file mode 100644 index 0000000..269410b --- /dev/null +++ b/Project.Web/Controllers/RoleController.cs @@ -0,0 +1,123 @@ +using Microsoft.AspNetCore.Mvc; +using Project.Application.DTOs.Incoming; +using Project.Application.Interfaces; + +namespace Project.Web.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class RoleController : ControllerBase + { + // FIELDS FOR CTOR + private readonly IRoleService _roleService; + + // CTOR + public RoleController(IRoleService roleService) + { + _roleService = roleService; + } + + // CREATE + [HttpPost] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task CreateRole([FromBody] CreatingRoleDto creatingRoleDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + try + { + var result = await _roleService.AddRoleAsync(creatingRoleDto); + + if (result != null) + { + var id = result.Id; + var createdResource = new { Id = id }; + var actionName = nameof(GetRoleById); + var routeValue = new { id = createdResource.Id }; + return CreatedAtAction(actionName, routeValue, createdResource); + } + else + { + return BadRequest("geht nix"); + } + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + } + + // READ ALL + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task GetRoles() + { + var roles = await _roleService.GetAllAsync(); + return Ok(roles); + } + + // READ BY ID + [HttpGet("id/{id}", Name = "GetRoleById")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetRoleById(int id) + { + if (id <= 0) + { + return BadRequest("Invalid Id"); + } + var role = await _roleService.GetByIdAsync(id); + if (role == null) + { + return NotFound(); + } + return Ok(role); + } + + // READ BY NAME + [HttpGet("name/{name}", Name = "GetRoleByName")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetRoleByName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return BadRequest("Name cannot be empty"); + } + var role = await _roleService.GetByNameAsync(name); + if (role == null) + { + return NotFound(); + } + return Ok(role); + } + + // UPDATE + [HttpPut("id/{id}", Name = "UpdateRole")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task UpdateRole(int id, UpdatingRoleDto updatingRoleDto) + { + var updated = await _roleService.UpdateRoleAsync(updatingRoleDto); + return Ok(updated); + } + + // DELETE + [HttpDelete("id/{id}", Name = "DeleteRole")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task DeleteRole([FromRoute] int id) + { + await _roleService.DeleteRoleAsync(id); + return Ok(); + } + } +} diff --git a/Project.Web/Controllers/UserController.cs b/Project.Web/Controllers/UserController.cs new file mode 100644 index 0000000..1df857e --- /dev/null +++ b/Project.Web/Controllers/UserController.cs @@ -0,0 +1,125 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Project.Application.DTOs.Incoming; +using Project.Application.Interfaces; + +namespace Project.Web.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class UserController : Controller + { + // FIELDS FOR CTOR + private readonly IUserService _userService; + + // CTOR + public UserController(IUserService userService) + { + _userService = userService; + } + + // CREATE + [HttpPost] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task CreateUser([FromBody] CreatingUserDto creatingUserDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + try + { + var result = await _userService.AddUserAsync(creatingUserDto); + + if (result != null) + { + var id = result.Id; + var createdResource = new { Id = id }; + var actionName = nameof(GetUserById); + var routeValue = new { id = createdResource.Id }; + return CreatedAtAction(actionName, routeValue, createdResource); + } + else + { + return BadRequest("Creation failed"); + } + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + } + + // READ ALL + //[Authorize(Roles = "Admin")] + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task GetUsers() + { + var users = await _userService.GetUsersAsync(); + return Ok(users); + } + + // READ BY ID + [HttpGet("id/{id}", Name = "GetUserById")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetUserById(int id) + { + if (id <= 0) + { + return BadRequest("Invalid Id"); + } + var user = await _userService.GetByIdAsync(id); + if (user == null) + { + return NotFound(); + } + return Ok(user); + } + + // READ BY USERNAME + [HttpGet("username/{username}", Name = "GetUserByUsername")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetUserByUsername(string username) + { + if (string.IsNullOrEmpty(username)) + { + return BadRequest("Username connot be empty"); + } + var user = await _userService.GetByUsernameAsync(username); + if(user == null) + { + return NotFound(); + } + return Ok(user); + } + + // UPDATE + [HttpPut("id/{id}", Name = "UpdateUser")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task UpdateUser(int id, UpdatingUserDto updatingUserDto) + { + var updated = await _userService.UpdateUserAsync(updatingUserDto); + return Ok(updated); + } + + // DELETE + [HttpDelete("id/{id}", Name = "DeleteUser")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task DeleteProduct([FromBody] int id) + { + await _userService.DeleteUserAsync(id); + return Ok(); + } + } +} diff --git a/Project.Web/Migrations/20240704072427_Sechste.Designer.cs b/Project.Web/Migrations/20240704072427_Sechste.Designer.cs new file mode 100644 index 0000000..05a6354 --- /dev/null +++ b/Project.Web/Migrations/20240704072427_Sechste.Designer.cs @@ -0,0 +1,212 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Project.Infrastructure; + +#nullable disable + +namespace Project.Web.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240704072427_Sechste")] + partial class Sechste + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Project.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("CATEGORY_NAME"); + + b.HasKey("Id"); + + b.ToTable("CATEGORY", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("RoleId"); + + b.ToTable("CATEGORY_ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PRODUCT_NAME"); + + b.Property("Price") + .HasColumnType("decimal(18,2)") + .HasColumnName("PRICE"); + + b.Property("Quantity") + .HasColumnType("int") + .HasColumnName("QUANTITY"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("PRODUCT", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ROLE"); + + b.HasKey("Id"); + + b.ToTable("ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("FIRST_NAME"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("LAST_NAME"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("USER", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Project.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Project.Domain.Entities.User", b => + { + b.HasOne("Project.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Project.Web/Migrations/20240704072427_Sechste.cs b/Project.Web/Migrations/20240704072427_Sechste.cs new file mode 100644 index 0000000..7ea6fbd --- /dev/null +++ b/Project.Web/Migrations/20240704072427_Sechste.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Project.Web.Migrations +{ + /// + public partial class Sechste : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PASSWORD", + schema: "dbo", + table: "USER", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PASSWORD", + schema: "dbo", + table: "USER"); + } + } +} diff --git a/Project.Web/Migrations/20240704090025_Siebte.Designer.cs b/Project.Web/Migrations/20240704090025_Siebte.Designer.cs new file mode 100644 index 0000000..2ed1a9a --- /dev/null +++ b/Project.Web/Migrations/20240704090025_Siebte.Designer.cs @@ -0,0 +1,196 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Project.Infrastructure; + +#nullable disable + +namespace Project.Web.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240704090025_Siebte")] + partial class Siebte + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Project.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("CATEGORY_NAME"); + + b.HasKey("Id"); + + b.ToTable("CATEGORY", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("RoleId"); + + b.ToTable("CATEGORY_ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PRODUCT_NAME"); + + b.Property("Price") + .HasColumnType("decimal(18,2)") + .HasColumnName("PRICE"); + + b.Property("Quantity") + .HasColumnType("int") + .HasColumnName("QUANTITY"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("PRODUCT", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ROLE"); + + b.HasKey("Id"); + + b.ToTable("ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("FIRST_NAME"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("LAST_NAME"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.ToTable("USER", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Project.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Project.Web/Migrations/20240704090025_Siebte.cs b/Project.Web/Migrations/20240704090025_Siebte.cs new file mode 100644 index 0000000..b8bedc7 --- /dev/null +++ b/Project.Web/Migrations/20240704090025_Siebte.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Project.Web.Migrations +{ + /// + public partial class Siebte : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_USER_ROLE_RoleId", + schema: "dbo", + table: "USER"); + + migrationBuilder.DropIndex( + name: "IX_USER_RoleId", + schema: "dbo", + table: "USER"); + + migrationBuilder.DropColumn( + name: "RoleId", + schema: "dbo", + table: "USER"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RoleId", + schema: "dbo", + table: "USER", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_USER_RoleId", + schema: "dbo", + table: "USER", + column: "RoleId"); + + migrationBuilder.AddForeignKey( + name: "FK_USER_ROLE_RoleId", + schema: "dbo", + table: "USER", + column: "RoleId", + principalSchema: "dbo", + principalTable: "ROLE", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/Project.Web/Migrations/20240705074345_Achte.Designer.cs b/Project.Web/Migrations/20240705074345_Achte.Designer.cs new file mode 100644 index 0000000..fb9b625 --- /dev/null +++ b/Project.Web/Migrations/20240705074345_Achte.Designer.cs @@ -0,0 +1,212 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Project.Infrastructure; + +#nullable disable + +namespace Project.Web.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240705074345_Achte")] + partial class Achte + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Project.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("CATEGORY_NAME"); + + b.HasKey("Id"); + + b.ToTable("CATEGORY", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("RoleId"); + + b.ToTable("CATEGORY_ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PRODUCT_NAME"); + + b.Property("Price") + .HasColumnType("decimal(18,2)") + .HasColumnName("PRICE"); + + b.Property("Quantity") + .HasColumnType("int") + .HasColumnName("QUANTITY"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("PRODUCT", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ROLE"); + + b.HasKey("Id"); + + b.ToTable("ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("FIRST_NAME"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("LAST_NAME"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("USER", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Project.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Project.Domain.Entities.User", b => + { + b.HasOne("Project.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Project.Web/Migrations/20240705074345_Achte.cs b/Project.Web/Migrations/20240705074345_Achte.cs new file mode 100644 index 0000000..cc9e297 --- /dev/null +++ b/Project.Web/Migrations/20240705074345_Achte.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Project.Web.Migrations +{ + /// + public partial class Achte : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RoleId", + schema: "dbo", + table: "USER", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_USER_RoleId", + schema: "dbo", + table: "USER", + column: "RoleId"); + + migrationBuilder.AddForeignKey( + name: "FK_USER_ROLE_RoleId", + schema: "dbo", + table: "USER", + column: "RoleId", + principalSchema: "dbo", + principalTable: "ROLE", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_USER_ROLE_RoleId", + schema: "dbo", + table: "USER"); + + migrationBuilder.DropIndex( + name: "IX_USER_RoleId", + schema: "dbo", + table: "USER"); + + migrationBuilder.DropColumn( + name: "RoleId", + schema: "dbo", + table: "USER"); + } + } +} diff --git a/Project.Web/Migrations/20240705115117_Neunte.Designer.cs b/Project.Web/Migrations/20240705115117_Neunte.Designer.cs new file mode 100644 index 0000000..fa2141c --- /dev/null +++ b/Project.Web/Migrations/20240705115117_Neunte.Designer.cs @@ -0,0 +1,212 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Project.Infrastructure; + +#nullable disable + +namespace Project.Web.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240705115117_Neunte")] + partial class Neunte + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Project.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("CATEGORY_NAME"); + + b.HasKey("Id"); + + b.ToTable("CATEGORY", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("RoleId"); + + b.ToTable("CATEGORY_ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PRODUCT_NAME"); + + b.Property("Price") + .HasColumnType("decimal(18,2)") + .HasColumnName("PRICE"); + + b.Property("Quantity") + .HasColumnType("int") + .HasColumnName("QUANTITY"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("PRODUCT", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ROLE"); + + b.HasKey("Id"); + + b.ToTable("ROLE", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("FIRST_NAME"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("LAST_NAME"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("USER", "dbo"); + }); + + modelBuilder.Entity("Project.Domain.Entities.CategoryRole", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Project.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Project.Domain.Entities.Product", b => + { + b.HasOne("Project.Domain.Entities.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Project.Domain.Entities.User", b => + { + b.HasOne("Project.Domain.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Project.Web/Migrations/20240705115117_Neunte.cs b/Project.Web/Migrations/20240705115117_Neunte.cs new file mode 100644 index 0000000..40a4c0a --- /dev/null +++ b/Project.Web/Migrations/20240705115117_Neunte.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Project.Web.Migrations +{ + /// + public partial class Neunte : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Project.Web/Migrations/ApplicationDbContextModelSnapshot.cs b/Project.Web/Migrations/ApplicationDbContextModelSnapshot.cs index f23e94a..fb078df 100644 --- a/Project.Web/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Project.Web/Migrations/ApplicationDbContextModelSnapshot.cs @@ -143,6 +143,11 @@ namespace Project.Web.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("LAST_NAME"); + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + b.Property("RoleId") .HasColumnType("int"); diff --git a/Project.Web/Program.cs b/Project.Web/Program.cs index 198645b..abbe8b3 100644 --- a/Project.Web/Program.cs +++ b/Project.Web/Program.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.EntityFrameworkCore; using Project.Application.Interfaces; using Project.Application.MappingProfiles; @@ -23,12 +24,37 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +builder.Services.AddScoped(); +//builder.Services.AddScoped(); + builder.Services.AddDbContext(options => { options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("Project.Web")); }); builder.Services.AddMemoryCache(); +builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(options => + { + options.Cookie.HttpOnly = true; + options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; + options.Cookie.SameSite = SameSiteMode.Strict; + options.LoginPath = "/api/auth/login"; + options.LogoutPath = "/api/auth/logout"; + }); + +//builder.Services.AddAuthorization(options => +//{ +// options.AddPolicy("AdminOnly", policy => +// policy.RequireRole("Admin")); +//}); + builder.Logging.ClearProviders(); builder.Logging.AddConsole(); @@ -43,6 +69,8 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); +app.UseAuthentication(); + app.UseAuthorization(); app.MapControllers(); diff --git a/Project.Web/Project.Web.csproj b/Project.Web/Project.Web.csproj index b894da4..4d6a08d 100644 --- a/Project.Web/Project.Web.csproj +++ b/Project.Web/Project.Web.csproj @@ -7,6 +7,7 @@ + diff --git a/Project.Web/Project.Web.csproj.user b/Project.Web/Project.Web.csproj.user index 2c8f5c1..031db34 100644 --- a/Project.Web/Project.Web.csproj.user +++ b/Project.Web/Project.Web.csproj.user @@ -2,7 +2,7 @@ https - ApiControllerEmptyScaffolder - root/Common/Api + MvcControllerEmptyScaffolder + root/Common/MVC/Controller \ No newline at end of file