├── HotelProject ├── appsettings.Development.json ├── Requirements │ ├── MinimumAgeRequirement.cs │ └── MinimumAgeHandler.cs ├── Helpers │ ├── SessionExtensions.cs │ └── SeedExtensions.cs ├── JobConfigurator.cs ├── appsettings.json ├── HotelProject.csproj ├── Properties │ └── launchSettings.json ├── Controllers │ ├── AccountsController.cs │ └── RoomsController.cs ├── GlobalErrorHandler.cs ├── Program.cs ├── ServiceExtensions.cs └── Services │ └── ViewRender.cs ├── BusinessLogic ├── Entities │ ├── Category.cs │ ├── RefreshToken.cs │ ├── Room.cs │ ├── User.cs │ └── Reservation.cs ├── DTOs │ ├── LogoutModel.cs │ ├── CategoryDto.cs │ ├── LoginModel.cs │ ├── UserTokens.cs │ ├── LoginResponseDto.cs │ ├── CreateRoomModel.cs │ ├── ReservationDto.cs │ ├── RegisterModel.cs │ └── RoomDto.cs ├── Interfaces │ ├── IViewRender.cs │ ├── IFileService.cs │ ├── IReservationService.cs │ ├── IRoomsService.cs │ ├── IJwtService.cs │ ├── IRepository.cs │ └── IAccountsService.cs ├── Helpers │ ├── MailJetSettings.cs │ └── JwtOptions.cs ├── Specifications │ ├── ReservationSpecs.cs │ ├── RefreshTokenSpecs.cs │ └── RoomSpecs.cs ├── HttpException.cs ├── Validators │ ├── RegisterModelValidator.cs │ ├── CategoryValidator.cs │ └── RoomValidator.cs ├── Profiles │ └── ApplicationProfile.cs ├── BusinessLogic.csproj ├── Services │ ├── MailJetSender.cs │ ├── LocalFileService.cs │ ├── ReservationsService.cs │ ├── RoomsService.cs │ ├── JwtService.cs │ └── AccountsService.cs ├── ServiceExtensions.cs ├── Errors.Designer.cs └── Errors.resx ├── DataAccess ├── Data │ ├── ShopDbContext.cs │ ├── Configurations │ │ └── RefreshTokenEntityConfigs.cs │ └── SampleContextFactory.cs ├── DataAccess.csproj ├── ServiceExtensions.cs ├── Repositories │ └── Repository.cs └── Migrations │ ├── 20240308204547_Initial.cs │ ├── ShopDbContextModelSnapshot.cs │ └── 20240308204547_Initial.Designer.cs ├── README.md ├── HotelProject.sln ├── .gitattributes └── .gitignore /HotelProject/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BusinessLogic/Entities/Category.cs: -------------------------------------------------------------------------------- 1 | namespace DataAccess.Data 2 | { 3 | public class Category 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public ICollection Rooms { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /BusinessLogic/DTOs/LogoutModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.DTOs 8 | { 9 | public class LogoutModel 10 | { 11 | public string RefreshToken { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BusinessLogic/Interfaces/IViewRender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.Interfaces 8 | { 9 | public interface IViewRender 10 | { 11 | string Render(string name, TModel model); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/CategoryDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.DTOs 8 | { 9 | public class CategoryDto 10 | { 11 | public int Id { get; set; } 12 | public string Name { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/LoginModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.DTOs 8 | { 9 | public class LoginModel 10 | { 11 | public string Email { get; set; } 12 | public string Password { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/UserTokens.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.DTOs 8 | { 9 | public class UserTokens 10 | { 11 | public string RefreshToken { get; set; } 12 | public string AccessToken { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BusinessLogic/Helpers/MailJetSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.Helpers 8 | { 9 | public class MailJetSettings 10 | { 11 | public string ApiKey { get; set; } 12 | public string ApiSecret { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/LoginResponseDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.DTOs 8 | { 9 | public class LoginResponseDto 10 | { 11 | public string AccessToken { get; set; } 12 | public string RefreshToken { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /HotelProject/Requirements/MinimumAgeRequirement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace HotelProject.Requirements 4 | { 5 | public class MinimumAgeRequirement : IAuthorizationRequirement 6 | { 7 | public MinimumAgeRequirement(int minimumAge) => 8 | MinimumAge = minimumAge; 9 | 10 | public int MinimumAge { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BusinessLogic/Interfaces/IFileService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BusinessLogic.Interfaces 9 | { 10 | public interface IFileService 11 | { 12 | Task SaveProductImage(IFormFile file); 13 | Task DeleteProductImage(string path); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/CreateRoomModel.cs: -------------------------------------------------------------------------------- 1 | using DataAccess.Data; 2 | using Microsoft.AspNetCore.Http; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BusinessLogic.DTOs 10 | { 11 | public class CreateRoomModel 12 | { 13 | public decimal Price { get; set; } 14 | public int CategoryId { get; set; } 15 | public IFormFile Image { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /BusinessLogic/Interfaces/IReservationService.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BusinessLogic.Interfaces 9 | { 10 | internal interface IReservationService 11 | { 12 | Task> GetAllByUser(string userId); 13 | Task Create(string userId, int id, DateTime date1, DateTime date2); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BusinessLogic/Helpers/JwtOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.Helpers 8 | { 9 | public class JwtOptions 10 | { 11 | public string Issuer { get; set; } 12 | public string Key { get; set; } 13 | public int AcceeTokenLifetimeInMinutes { get; set; } 14 | public int RefreshTokenLifetimeInDays { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BusinessLogic/Entities/RefreshToken.cs: -------------------------------------------------------------------------------- 1 | using DataAccess.Data; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BusinessLogic.Entities 9 | { 10 | public class RefreshToken 11 | { 12 | public int Id { get; set; } 13 | public string Token { get; set; } 14 | public DateTime CreationDate { get; set; } 15 | public string UserId { get; set; } 16 | public User? User { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/ReservationDto.cs: -------------------------------------------------------------------------------- 1 | using DataAccess.Data; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BusinessLogic.DTOs 9 | { 10 | public class ReservationDto 11 | { 12 | public int Id { get; set; } 13 | public string UserId { get; set; } 14 | public DateOnly CheckInDate { get; set; } 15 | public DateOnly CheckOutDate { get; set; } 16 | public int RoomId { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/RegisterModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BusinessLogic.DTOs 8 | { 9 | public class RegisterModel 10 | { 11 | public string Name { get; set; } 12 | public string Surname { get; set; } 13 | public string Email { get; set; } 14 | public string Password { get; set; } 15 | public DateTime Birthdate { get; set; } 16 | public string? Number { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BusinessLogic/Entities/Room.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DataAccess.Data 9 | { 10 | public class Room 11 | { 12 | public int Id { get; set; } 13 | public decimal Price { get; set; } 14 | public int CategoryId { get; set; } 15 | public Category Category { get; set; } 16 | public string ImageUrl { get; set; } 17 | public ICollection? Reservations { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BusinessLogic/DTOs/RoomDto.cs: -------------------------------------------------------------------------------- 1 | using DataAccess.Data; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BusinessLogic.DTOs 9 | { 10 | public class RoomDto 11 | { 12 | public int Id { get; set; } 13 | public decimal Price { get; set; } 14 | public int CategoryId { get; set; } 15 | public string? CategoryName { get; set; } 16 | public string ImageUrl { get; set; } 17 | public ICollection? Reservations { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /HotelProject/Helpers/SessionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace HotelProject.Helpers 4 | { 5 | public static class SessionExtensions 6 | { 7 | public static void Set(this ISession session, string key, T value) 8 | { 9 | session.SetString(key, JsonSerializer.Serialize(value)); 10 | } 11 | 12 | public static T? Get(this ISession session, string key) 13 | { 14 | var value = session.GetString(key); 15 | return value == null ? default : JsonSerializer.Deserialize(value); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BusinessLogic/Entities/User.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.Entities; 2 | using Microsoft.AspNetCore.Identity; 3 | 4 | namespace DataAccess.Data 5 | { 6 | public enum ClientType { Node, Regular, Premium } 7 | public class User : IdentityUser 8 | { 9 | public string Name { get; set; } 10 | public string Surname { get; set; } 11 | public DateTime Birthdate { get; set; } 12 | public ICollection? Reservations { get; set; } 13 | public ClientType ClientType { get; set; } 14 | public ICollection? RefreshTokens { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /HotelProject/JobConfigurator.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.Interfaces; 2 | using Hangfire; 3 | 4 | namespace HotelProject 5 | { 6 | public class JobConfigurator 7 | { 8 | public static void AddJobs() 9 | { 10 | RemoveExpiredTokensJob(); 11 | } 12 | public static void RemoveExpiredTokensJob() 13 | { 14 | RecurringJob.AddOrUpdate( 15 | nameof(RemoveExpiredTokensJob), 16 | (service) => service.RemoveExpiredRefreshTokens(), 17 | Cron.Weekly(DayOfWeek.Monday, 3)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BusinessLogic/Entities/Reservation.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualBasic; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DataAccess.Data 9 | { 10 | public class Reservation 11 | { 12 | public int Id { get; set; } 13 | public string UserId { get; set; } 14 | public User User { get; set; } 15 | public DateTime CheckInDate { get; set; } 16 | public DateTime CheckOutDate { get; set; } 17 | public int RoomId { get; set; } 18 | public Room Room { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DataAccess/Data/ShopDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | using System.Reflection; 4 | 5 | 6 | namespace DataAccess.Data 7 | { 8 | public class ShopDbContext : IdentityDbContext 9 | { 10 | public ShopDbContext(DbContextOptions options) : base(options) { } 11 | 12 | protected override void OnModelCreating(ModelBuilder modelBuilder) 13 | { 14 | base.OnModelCreating(modelBuilder); 15 | 16 | modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BusinessLogic/Interfaces/IRoomsService.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BusinessLogic.Interfaces 9 | { 10 | public interface IRoomsService 11 | { 12 | Task> GetAll(); 13 | Task> Get(IEnumerable ids); 14 | Task Get(int id); 15 | IEnumerable GetAllCategories(); 16 | void Create(CreateRoomModel room); 17 | void Edit(RoomDto room); 18 | void Delete(int id); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BusinessLogic/Interfaces/IJwtService.cs: -------------------------------------------------------------------------------- 1 | using DataAccess.Data; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Claims; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BusinessLogic.Interfaces 10 | { 11 | public interface IJwtService 12 | { 13 | // ------- Access Token 14 | IEnumerable GetClaims(User user); 15 | string CreateToken(IEnumerable claims); 16 | 17 | // ------- Refresh Token 18 | string CreateRefreshToken(); 19 | IEnumerable GetClaimsFromExpiredToken(string token); 20 | DateTime GetLastValidRefreshTokenDate(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BusinessLogic/Specifications/ReservationSpecs.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.Specification; 2 | using DataAccess.Data; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using static Microsoft.EntityFrameworkCore.DbLoggerCategory; 9 | 10 | namespace BusinessLogic.Specifications 11 | { 12 | internal static class ReservationSpecs 13 | { 14 | internal class ByUser : Specification 15 | { 16 | public ByUser(string userId) 17 | { 18 | Query 19 | .Where(x => x.UserId == userId) 20 | .Include(x => x.Room); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DataAccess/Data/Configurations/RefreshTokenEntityConfigs.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.Entities; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | using Microsoft.EntityFrameworkCore; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DataAccess.Data.Configurations 11 | { 12 | public class RefreshTokenEntityConfigs : IEntityTypeConfiguration 13 | { 14 | public void Configure(EntityTypeBuilder builder) 15 | { 16 | builder.HasKey(x => x.Id); 17 | builder.ToTable("RefreshTokens"); 18 | builder.HasOne(x => x.User).WithMany(x => x.RefreshTokens).HasForeignKey(x => x.UserId); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /HotelProject/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ConnectionStrings": { 10 | "LocalDb": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=HotelBooking;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False" 11 | }, 12 | 13 | "MailJetSettings": { 14 | "ApiKey": "62cd12be131e19e25a5d1cf0c2c4a356", 15 | "ApiSecret": "91b7070cc8f2a9294abaf6b424f51c0c" 16 | }, 17 | 18 | "JwtOptions": { 19 | "Issuer": "HotelBooking", 20 | "Key": "bcafa5bb8fcea43cd62512a8b4f0d540", 21 | "AcceeTokenLifetimeInMinutes": 10, 22 | "RefreshTokenLifetimeInDays": 4 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BusinessLogic/Interfaces/IRepository.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.Specification; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | 10 | namespace DataAccess.Repositories 11 | { 12 | public interface IRepository where TEntity : class 13 | { 14 | IEnumerable GetAll(); 15 | TEntity GetById(object id); 16 | void Insert(TEntity entity); 17 | void Delete(object id); 18 | void Delete(TEntity entityToDelete); 19 | void Update(TEntity entityToUpdate); 20 | Task GetItemBySpec(ISpecification specification); 21 | Task> GetListBySpec(ISpecification specification); 22 | 23 | void Save(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BusinessLogic/Interfaces/IAccountsService.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BusinessLogic.Interfaces 9 | { 10 | public interface IAccountsService 11 | { 12 | Task Register(RegisterModel model); 13 | Task Login(LoginModel model); 14 | Task Logout(string refreshToken); 15 | Task RefreshTokens(UserTokens tokens); 16 | 17 | Task RemoveExpiredRefreshTokens(); 18 | 19 | } 20 | 21 | public class ResetToken 22 | { 23 | public string Token { get; set; } 24 | } 25 | public class ResetPasswordModel 26 | { 27 | public string NewPassword { get; set; } 28 | public string Token { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Hotel Management Web API 2 | Hotel Management Web API 3 | This project is a Hotel Management Web API built using ASP.NET Core. It provides endpoints for user registration, authentication, and managing hotel rooms. The API supports CRUD operations on rooms and allows users to interact with room data. 4 | 5 | ## Features 6 | - User registration and login (authentication and authorization). 7 | - CRUD operations for managing hotel rooms. 8 | - Secure endpoints using JWT for user authentication. 9 | - Role-based access control. 10 | 11 | ## Technologies Used 12 | - ASP.NET Core: For building the web API. 13 | - Entity Framework Core (EF Core): For database interactions. 14 | - Identity: For user authentication and authorization. 15 | - JWT (JSON Web Tokens): For secure authentication. 16 | - SQL Server: For the database. 17 | - Swagger: For API documentation. 18 | 19 | -------------------------------------------------------------------------------- /BusinessLogic/HttpException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Runtime.Serialization; 3 | 4 | namespace BusinessLogic 5 | { 6 | [Serializable] 7 | public class HttpException : Exception 8 | { 9 | public HttpStatusCode Status { get; set; } 10 | public HttpException(HttpStatusCode status) 11 | { 12 | Status = status; 13 | } 14 | 15 | public HttpException(string? message, HttpStatusCode status) : base(message) 16 | { 17 | Status = status; 18 | } 19 | 20 | public HttpException(string? message, HttpStatusCode status, Exception? innerException) : base(message, innerException) 21 | { 22 | Status = status; 23 | } 24 | 25 | protected HttpException(SerializationInfo info, StreamingContext context) : base(info, context) 26 | { 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /BusinessLogic/Specifications/RefreshTokenSpecs.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.Specification; 2 | using BusinessLogic.Entities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using static Microsoft.EntityFrameworkCore.DbLoggerCategory; 9 | 10 | namespace BusinessLogic.Specifications 11 | { 12 | internal static class RefreshTokenSpecs 13 | { 14 | internal class ByToken : Specification 15 | { 16 | public ByToken(string value) 17 | { 18 | Query.Where(x => x.Token == value); 19 | } 20 | } 21 | internal class CreatedBy : Specification 22 | { 23 | public CreatedBy(DateTime date) 24 | { 25 | 26 | Query.Where(x => x.CreationDate < date); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BusinessLogic/Validators/RegisterModelValidator.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using FluentValidation; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BusinessLogic.Validators 10 | { 11 | public class RegisterModelValidator : AbstractValidator 12 | { 13 | public RegisterModelValidator() 14 | { 15 | RuleFor(x => x.Email) 16 | .NotEmpty() 17 | .EmailAddress(); 18 | 19 | RuleFor(x => x.Password) 20 | .NotEmpty(); 21 | 22 | RuleFor(x => x.Birthdate) 23 | .NotEmpty() 24 | .GreaterThan(new DateTime(1900, 1, 1)).WithMessage("Birthdate must be bigger than 1900.") 25 | .LessThan(DateTime.Now).WithMessage("Birthdate cannot be futura date."); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DataAccess/DataAccess.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /DataAccess/Data/SampleContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Design; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.Extensions.Configuration; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DataAccess.Data 11 | { 12 | public class SampleContextFactory : IDesignTimeDbContextFactory 13 | { 14 | public ShopDbContext CreateDbContext(string[] args) 15 | { 16 | var optionsBuilder = new DbContextOptionsBuilder(); 17 | 18 | ConfigurationBuilder builder = new ConfigurationBuilder(); 19 | builder.SetBasePath(Directory.GetCurrentDirectory()); 20 | builder.AddJsonFile("appsettings.json"); 21 | IConfigurationRoot config = builder.Build(); 22 | 23 | string? connectionString = config.GetConnectionString("LocalDb"); 24 | optionsBuilder.UseSqlServer(connectionString); 25 | return new ShopDbContext(optionsBuilder.Options); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /DataAccess/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using DataAccess.Data; 2 | using DataAccess.Repositories; 3 | using Microsoft.AspNetCore.Identity; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace DataAccess 8 | { 9 | public static class ServiceExtensions 10 | { 11 | public static void AddDbContext(this IServiceCollection services, string connectionString) 12 | { 13 | services.AddDbContext(opts => 14 | opts.UseSqlServer(connectionString)); 15 | } 16 | 17 | public static void AddRepositories(this IServiceCollection services) 18 | { 19 | services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); 20 | } 21 | 22 | public static void AddIdentity(this IServiceCollection services) 23 | { 24 | services.AddIdentity(options => 25 | { 26 | options.SignIn.RequireConfirmedAccount = false; 27 | }) 28 | .AddDefaultTokenProviders() 29 | .AddEntityFrameworkStores(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /HotelProject/HotelProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /BusinessLogic/Profiles/ApplicationProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BusinessLogic.DTOs; 3 | using BusinessLogic.Interfaces; 4 | using DataAccess.Data; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using static System.Runtime.InteropServices.JavaScript.JSType; 11 | 12 | namespace BusinessLogic.Profiles 13 | { 14 | public class ApplicationProfile : Profile 15 | { 16 | public ApplicationProfile(IFileService fileService) 17 | { 18 | CreateMap() 19 | .ForMember(x => x.Category, opt => opt.Ignore()); 20 | CreateMap(); 21 | 22 | CreateMap().ReverseMap(); 23 | CreateMap().ReverseMap(); 24 | 25 | CreateMap() 26 | .ForMember(x => x.ImageUrl, opt => opt.MapFrom(src => fileService.SaveProductImage(src.Image).Result)); 27 | 28 | CreateMap() 29 | .ForMember(x => x.UserName, opts => opts.MapFrom(s => s.Email)); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BusinessLogic/Validators/CategoryValidator.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using FluentValidation; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BusinessLogic.Validators 10 | { 11 | public class CategoryValidator : AbstractValidator 12 | { 13 | public CategoryValidator() 14 | { 15 | RuleFor(x => x.Name) 16 | .NotEmpty() 17 | .MinimumLength(2) 18 | .Matches("[A-Z].*").WithMessage("{PropertyName} must starts with uppercase letter."); 19 | } 20 | 21 | private static bool LinkMustBeAUri(string link) 22 | { 23 | if (string.IsNullOrWhiteSpace(link)) 24 | { 25 | return false; 26 | } 27 | 28 | //Courtesy of @Pure.Krome's comment and https://stackoverflow.com/a/25654227/563532 29 | Uri outUri; 30 | return Uri.TryCreate(link, UriKind.Absolute, out outUri) 31 | && (outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BusinessLogic/Specifications/RoomSpecs.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.Specification; 2 | using DataAccess.Data; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using static Microsoft.EntityFrameworkCore.DbLoggerCategory; 9 | 10 | namespace BusinessLogic.Specifications 11 | { 12 | internal static class RoomSpecs 13 | { 14 | internal class ById : Specification 15 | { 16 | public ById(int id) 17 | { 18 | Query 19 | .Where(x => x.Id == id) 20 | .Include(x => x.Category); 21 | } 22 | } 23 | internal class All : Specification 24 | { 25 | public All() 26 | { 27 | Query.Include(x => x.Category); 28 | } 29 | } 30 | internal class ByIds : Specification 31 | { 32 | public ByIds(IEnumerable ids) 33 | { 34 | Query 35 | .Where(x => ids.Contains(x.Id)) 36 | .Include(x => x.Category); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /HotelProject/Requirements/MinimumAgeHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using System.Security.Claims; 3 | namespace HotelProject.Requirements 4 | { 5 | public class MinimumAgeHandler : AuthorizationHandler 6 | { 7 | protected override Task HandleRequirementAsync( 8 | AuthorizationHandlerContext context, MinimumAgeRequirement requirement) 9 | { 10 | var dateOfBirthClaim = context.User.FindFirst( 11 | c => c.Type == ClaimTypes.DateOfBirth); 12 | 13 | if (dateOfBirthClaim is null) 14 | { 15 | return Task.CompletedTask; 16 | } 17 | 18 | var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value); 19 | int calculatedAge = DateTime.Today.Year - dateOfBirth.Year; 20 | if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge)) 21 | { 22 | calculatedAge--; 23 | } 24 | 25 | if (calculatedAge >= requirement.MinimumAge) 26 | { 27 | context.Succeed(requirement); 28 | } 29 | 30 | return Task.CompletedTask; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /HotelProject/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:64033", 8 | "sslPort": 44390 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "http://localhost:5240", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "applicationUrl": "https://localhost:7294;http://localhost:5240", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "swagger", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BusinessLogic/Validators/RoomValidator.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using FluentValidation; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BusinessLogic.Validators 10 | { 11 | public class RoomValidator : AbstractValidator 12 | { 13 | public RoomValidator() 14 | { 15 | RuleFor(x => x.Price) 16 | .NotEmpty() 17 | .GreaterThanOrEqualTo(0).WithMessage("{PropertyName} can not be negative."); 18 | 19 | RuleFor(x => x.CategoryId) 20 | .NotEmpty(); 21 | 22 | RuleFor(x => x.ImageUrl) 23 | .NotEmpty() 24 | .Must(LinkMustBeAUri).WithMessage("{PropertyName} must be a valid URL address."); 25 | 26 | } 27 | 28 | private static bool LinkMustBeAUri(string link) 29 | { 30 | if (string.IsNullOrWhiteSpace(link)) 31 | { 32 | return false; 33 | } 34 | 35 | //Courtesy of @Pure.Krome's comment and https://stackoverflow.com/a/25654227/563532 36 | Uri outUri; 37 | return Uri.TryCreate(link, UriKind.Absolute, out outUri) 38 | && (outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /HotelProject/Controllers/AccountsController.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using BusinessLogic.Interfaces; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace HotelProject.Controllers 6 | { 7 | [Route("api/[controller]")] 8 | [ApiController] 9 | public class AccountsController : ControllerBase 10 | { 11 | private readonly IAccountsService accountsService; 12 | 13 | public AccountsController(IAccountsService accountsService) 14 | { 15 | this.accountsService = accountsService; 16 | } 17 | 18 | [HttpPost("register")] 19 | public async Task Register([FromBody] RegisterModel model) 20 | { 21 | await accountsService.Register(model); 22 | return Ok(); 23 | } 24 | 25 | [HttpPost("login")] 26 | public async Task Login([FromBody] LoginModel model) 27 | { 28 | return Ok(await accountsService.Login(model)); 29 | } 30 | 31 | [HttpPost("refreshTokens")] 32 | public async Task RefreshTokens(UserTokens tokens) 33 | { 34 | return Ok(await accountsService.RefreshTokens(tokens)); 35 | } 36 | 37 | [HttpPost("logout")] 38 | public async Task Logout(LogoutModel model) 39 | { 40 | await accountsService.Logout(model.RefreshToken); 41 | return Ok(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /BusinessLogic/BusinessLogic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | True 25 | True 26 | Errors.resx 27 | 28 | 29 | 30 | 31 | 32 | ResXFileCodeGenerator 33 | Errors.Designer.cs 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /BusinessLogic/Services/MailJetSender.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.Helpers; 2 | using Mailjet.Client; 3 | using Mailjet.Client.Resources; 4 | using Microsoft.AspNetCore.Identity.UI.Services; 5 | using Microsoft.Extensions.Configuration; 6 | using Newtonsoft.Json.Linq; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace BusinessLogic.Services 14 | { 15 | public class MailJetSender : IEmailSender 16 | { 17 | private readonly IConfiguration _configuration; 18 | public MailJetSender(IConfiguration configuration) 19 | { 20 | _configuration = configuration; 21 | } 22 | public async Task SendEmailAsync(string email, string subject, string htmlMessage) 23 | { 24 | MailJetSettings? settings = _configuration.GetSection(nameof(MailJetSettings)).Get(); 25 | if (settings == null) throw new ArgumentNullException(nameof(settings)); 26 | 27 | MailjetClient client = new MailjetClient(settings.ApiKey, settings.ApiSecret); 28 | MailjetRequest request = new MailjetRequest 29 | { 30 | Resource = Send.Resource, 31 | } 32 | .Property(Send.FromEmail, "wladnaz@ukr.net") 33 | .Property(Send.FromName, "Vlad") 34 | .Property(Send.Subject, subject) 35 | //.Property(Send.TextPart, ) 36 | .Property(Send.HtmlPart, htmlMessage) 37 | .Property(Send.Recipients, new JArray { 38 | new JObject { 39 | {"Email", email} 40 | } 41 | }); 42 | 43 | await client.PostAsync(request); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /BusinessLogic/Services/LocalFileService.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.Interfaces; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Http; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BusinessLogic.Services 11 | { 12 | public class LocalFileService : IFileService 13 | { 14 | private const string imageFolder = "images"; 15 | private readonly IWebHostEnvironment environment; 16 | 17 | public LocalFileService(IWebHostEnvironment environment) 18 | { 19 | this.environment = environment; 20 | } 21 | 22 | public Task DeleteProductImage(string path) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | 27 | public async Task SaveProductImage(IFormFile file) 28 | { 29 | // get image destination path 30 | string root = environment.WebRootPath; // wwwroot 31 | string name = Guid.NewGuid().ToString(); // random name 32 | string extension = Path.GetExtension(file.FileName); // get original extension 33 | string fullName = name + extension; // full name: name.ext 34 | 35 | // create destination image file path 36 | string imagePath = Path.Combine(imageFolder, fullName); 37 | string imageFullPath = Path.Combine(root, imagePath); 38 | 39 | // save image on the folder 40 | using (FileStream fs = new FileStream(imageFullPath, FileMode.Create)) 41 | { 42 | await file.CopyToAsync(fs); 43 | } 44 | 45 | // return image file path 46 | return Path.DirectorySeparatorChar + imagePath; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /HotelProject/GlobalErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic; 2 | using FluentValidation; 3 | using System.Net; 4 | using System.Text.Json; 5 | 6 | namespace HotelProject 7 | { 8 | public class GlobalErrorHandler 9 | { 10 | private readonly RequestDelegate _next; 11 | 12 | public GlobalErrorHandler(RequestDelegate next) 13 | { 14 | _next = next; 15 | } 16 | 17 | public async Task Invoke(HttpContext context) 18 | { 19 | try 20 | { 21 | await _next(context); 22 | } 23 | catch (HttpException httpError) 24 | { 25 | await CreateResponse(context, httpError.Status, httpError.Message); 26 | } 27 | catch (ValidationException validationError) 28 | { 29 | await CreateResponse(context, HttpStatusCode.BadRequest, validationError.Message); 30 | } 31 | catch (KeyNotFoundException error) 32 | { 33 | await CreateResponse(context, HttpStatusCode.NotFound, error.Message); 34 | } 35 | catch (Exception error) 36 | { 37 | await CreateResponse(context, HttpStatusCode.InternalServerError, error.Message); 38 | } 39 | } 40 | 41 | private async Task CreateResponse(HttpContext context, 42 | HttpStatusCode statusCode = HttpStatusCode.InternalServerError, 43 | string message = "Unknown error type!") 44 | { 45 | context.Response.ContentType = "application/json"; 46 | context.Response.StatusCode = (int)statusCode; 47 | var result = JsonSerializer.Serialize(new { message }); 48 | await context.Response.WriteAsync(result); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /HotelProject/Helpers/SeedExtensions.cs: -------------------------------------------------------------------------------- 1 | using DataAccess.Data; 2 | using Microsoft.AspNetCore.Identity; 3 | using System.Reflection; 4 | 5 | namespace HotelProject.Helpers 6 | { 7 | public static class Roles 8 | { 9 | public const string ADMIN = "admin"; 10 | public const string USER = "user"; 11 | } 12 | 13 | public static class Seeder 14 | { 15 | public static async Task SeedRoles(this IServiceProvider app) 16 | { 17 | var roleManager = app.GetRequiredService>(); 18 | 19 | var roles = typeof(Roles).GetFields( 20 | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) 21 | .Select(x => (string)x.GetValue(null)!); 22 | 23 | foreach (var role in roles) 24 | { 25 | if (!await roleManager.RoleExistsAsync(role)) 26 | { 27 | await roleManager.CreateAsync(new IdentityRole(role)); 28 | } 29 | } 30 | } 31 | 32 | public static async Task SeedAdmin(this IServiceProvider app) 33 | { 34 | var userManager = app.GetRequiredService>(); 35 | 36 | const string USERNAME = "myadmin@myadmin.com"; 37 | const string PASSWORD = "Admin1@"; 38 | 39 | var existingUser = await userManager.FindByNameAsync(USERNAME); 40 | 41 | if (existingUser == null) 42 | { 43 | var user = new User 44 | { 45 | UserName = USERNAME, 46 | Email = USERNAME 47 | }; 48 | 49 | var result = await userManager.CreateAsync(user, PASSWORD); 50 | 51 | if (result.Succeeded) 52 | await userManager.AddToRoleAsync(user, Roles.ADMIN); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /BusinessLogic/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BusinessLogic.Interfaces; 3 | using BusinessLogic.Profiles; 4 | using BusinessLogic.Services; 5 | using FluentValidation; 6 | using Microsoft.AspNetCore.Identity.UI.Services; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace BusinessLogic 15 | { 16 | public static class ServiceExtensions 17 | { 18 | public static void AddAutoMapper(this IServiceCollection services) 19 | { 20 | //services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); 21 | services.AddSingleton(provider => new MapperConfiguration(cfg => 22 | { 23 | cfg.AddProfile(new ApplicationProfile(provider.CreateScope().ServiceProvider.GetService()!)); 24 | }).CreateMapper()); 25 | } 26 | 27 | public static void AddFluentValidators(this IServiceCollection services) 28 | { 29 | //services.AddFluentValidationAutoValidation(); 30 | // enable client-side validation 31 | //services.AddFluentValidationClientsideAdapters(); 32 | // Load an assembly reference rather than using a marker type. 33 | services.AddValidatorsFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()); 34 | } 35 | 36 | public static void AddCustomServices(this IServiceCollection services) 37 | { 38 | services.AddScoped(); 39 | services.AddScoped(); 40 | services.AddScoped(); 41 | services.AddScoped(); 42 | services.AddScoped(); 43 | services.AddScoped(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /BusinessLogic/Services/ReservationsService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BusinessLogic.DTOs; 3 | using BusinessLogic.Interfaces; 4 | using BusinessLogic.Specifications; 5 | using DataAccess.Data; 6 | using DataAccess.Repositories; 7 | using Microsoft.AspNetCore.Cors.Infrastructure; 8 | using Microsoft.AspNetCore.Identity.UI.Services; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace BusinessLogic.Services 16 | { 17 | internal class ReservationsService : IReservationService 18 | { 19 | private readonly IMapper mapper; 20 | private readonly IRepository reservationR; 21 | private readonly IRepository roomR; 22 | private readonly IEmailSender emailSender; 23 | 24 | public ReservationsService(IMapper mapper, 25 | IRepository reservationR, 26 | IRepository roomR, 27 | IEmailSender emailSender) 28 | { 29 | this.mapper = mapper; 30 | this.reservationR = reservationR; 31 | this.roomR = roomR; 32 | this.emailSender = emailSender; 33 | } 34 | 35 | public async Task Create(string userId, int id, DateTime date1, DateTime date2) 36 | { 37 | var reservation = new Reservation() 38 | { 39 | CheckInDate = date1, 40 | CheckOutDate = date2, 41 | UserId = userId, 42 | RoomId = id 43 | }; 44 | 45 | reservationR.Insert(reservation); 46 | reservationR.Save(); 47 | } 48 | 49 | public async Task> GetAllByUser(string userId) 50 | { 51 | var items = await reservationR.GetListBySpec(new ReservationSpecs.ByUser(userId)); 52 | return mapper.Map>(items); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /HotelProject/Controllers/RoomsController.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.DTOs; 2 | using BusinessLogic.Interfaces; 3 | using HotelProject.Helpers; 4 | using Microsoft.AspNetCore.Authentication.JwtBearer; 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace HotelProject.Controllers 9 | { 10 | [Route("api/[controller]")] 11 | [ApiController] 12 | public class RoomsController : ControllerBase 13 | { 14 | private readonly IRoomsService roomsService; 15 | 16 | public RoomsController(IRoomsService roomsService) 17 | { 18 | this.roomsService = roomsService; 19 | } 20 | 21 | [HttpGet("all")] 22 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = Policies.ADULT)] 23 | public async Task Get() 24 | { 25 | return Ok(await roomsService.GetAll()); 26 | } 27 | 28 | //[Authorize] // based on cookies 29 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] // based on JWT 30 | [HttpGet("{id:int}")] 31 | public async Task Get([FromRoute] int id) 32 | { 33 | return Ok(await roomsService.Get(id)); 34 | } 35 | 36 | [HttpPost] 37 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = Policies.PREMIUM_CLIENT)] 38 | public IActionResult Create([FromForm] CreateRoomModel model) 39 | { 40 | roomsService.Create(model); 41 | return Ok(); 42 | } 43 | 44 | 45 | [HttpPut] 46 | public IActionResult Edit([FromBody] RoomDto model) 47 | { 48 | roomsService.Edit(model); 49 | return Ok(); 50 | } 51 | 52 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = Roles.ADMIN)] 53 | [HttpDelete("{id:int}")] 54 | public IActionResult Delete([FromRoute] int id) 55 | { 56 | roomsService.Delete(id); 57 | return Ok(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /HotelProject.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33424.131 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotelProject", "HotelProject\HotelProject.csproj", "{34D08DA4-880E-4A2C-B2C6-6439D5B4002F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccess", "DataAccess\DataAccess.csproj", "{E6C06B2F-0588-4239-A699-4175F8E66CF3}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLogic", "BusinessLogic\BusinessLogic.csproj", "{3EA5CE92-FBEC-44D8-B328-F88A1CECF6A1}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {34D08DA4-880E-4A2C-B2C6-6439D5B4002F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {34D08DA4-880E-4A2C-B2C6-6439D5B4002F}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {34D08DA4-880E-4A2C-B2C6-6439D5B4002F}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {34D08DA4-880E-4A2C-B2C6-6439D5B4002F}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {E6C06B2F-0588-4239-A699-4175F8E66CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {E6C06B2F-0588-4239-A699-4175F8E66CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {E6C06B2F-0588-4239-A699-4175F8E66CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {E6C06B2F-0588-4239-A699-4175F8E66CF3}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {3EA5CE92-FBEC-44D8-B328-F88A1CECF6A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {3EA5CE92-FBEC-44D8-B328-F88A1CECF6A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {3EA5CE92-FBEC-44D8-B328-F88A1CECF6A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {3EA5CE92-FBEC-44D8-B328-F88A1CECF6A1}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {D29B2BFC-3A8D-4837-910E-C3F7048BC175} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /HotelProject/Program.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using HotelProject.Helpers; 3 | using HotelProject.Services; 4 | using HotelProject; 5 | using Microsoft.AspNetCore.Cors.Infrastructure; 6 | using DataAccess; 7 | using BusinessLogic; 8 | using Hangfire; 9 | 10 | namespace HotelProject 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | var builder = WebApplication.CreateBuilder(args); 17 | 18 | var connStr = builder.Configuration.GetConnectionString("LocalDb")!; 19 | 20 | // Add services to the container. 21 | 22 | builder.Services.AddControllers(); 23 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 24 | builder.Services.AddEndpointsApiExplorer(); 25 | // TODO: configure swagger with JWT 26 | builder.Services.AddSwaggerGen(); 27 | builder.Services.AddJWT(builder.Configuration); 28 | builder.Services.AddRequirements(); 29 | 30 | builder.Services.AddDbContext(connStr); 31 | builder.Services.AddIdentity(); 32 | builder.Services.AddRepositories(); 33 | 34 | builder.Services.AddAutoMapper(); 35 | builder.Services.AddFluentValidators(); 36 | 37 | builder.Services.AddCustomServices(); 38 | //builder.Services.AddScoped(); 39 | builder.Services.AddHangfire(connStr); 40 | var app = builder.Build(); 41 | 42 | using (var scope = app.Services.CreateScope()) 43 | { 44 | scope.ServiceProvider.SeedRoles().Wait(); 45 | scope.ServiceProvider.SeedAdmin().Wait(); 46 | } 47 | 48 | // Configure the HTTP request pipeline. 49 | if (app.Environment.IsDevelopment()) 50 | { 51 | app.UseSwagger(); 52 | app.UseSwaggerUI(); 53 | } 54 | 55 | app.UseHttpsRedirection(); 56 | app.UseStaticFiles(); 57 | 58 | app.UseMiddleware(); 59 | 60 | app.UseAuthorization(); 61 | 62 | app.UseHangfireDashboard("/dash"); 63 | JobConfigurator.AddJobs(); 64 | 65 | app.MapControllers(); 66 | 67 | app.Run(); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /HotelProject/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using BusinessLogic.Helpers; 3 | using DataAccess.Data; 4 | using BusinessLogic.Entities; 5 | using HotelProject.Requirements; 6 | using Microsoft.AspNetCore.Authentication.JwtBearer; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.IdentityModel.Tokens; 9 | using Hangfire; 10 | 11 | namespace HotelProject 12 | { 13 | public static class Policies 14 | { 15 | public const string PREMIUM_CLIENT = "PremiumClient"; 16 | public const string ADULT = "Adult"; 17 | } 18 | public static class ServiceExtensions 19 | { 20 | public static void AddHangfire(this IServiceCollection services, string connectionString) 21 | { 22 | services.AddHangfire(config => 23 | { 24 | config.UseSqlServerStorage(connectionString); 25 | }); 26 | 27 | services.AddHangfireServer(); 28 | } 29 | public static void AddRequirements(this IServiceCollection services) 30 | { 31 | services.AddSingleton(); 32 | } 33 | public static void AddJWT(this IServiceCollection services, IConfiguration configuration) 34 | { 35 | var jwtOpts = configuration.GetSection(nameof(JwtOptions)).Get()!; 36 | 37 | services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 38 | .AddJwtBearer(o => 39 | { 40 | o.TokenValidationParameters = new TokenValidationParameters 41 | { 42 | ValidateIssuer = true, 43 | ValidateAudience = false, 44 | ValidateLifetime = true, 45 | ValidateIssuerSigningKey = true, 46 | ValidIssuer = jwtOpts.Issuer, 47 | IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOpts.Key)), 48 | ClockSkew = TimeSpan.Zero 49 | }; 50 | }); 51 | 52 | services.AddAuthorization(options => 53 | { 54 | options.AddPolicy(Policies.PREMIUM_CLIENT, policy => 55 | policy.RequireClaim("ClientType", ClientType.Premium.ToString())); 56 | }); 57 | 58 | services.AddAuthorization(options => 59 | { 60 | options.AddPolicy(Policies.ADULT, policy => 61 | policy.Requirements.Add(new MinimumAgeRequirement(18))); 62 | }); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /HotelProject/Services/ViewRender.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.Interfaces; 2 | using Microsoft.AspNetCore.Mvc.Abstractions; 3 | using Microsoft.AspNetCore.Mvc.ModelBinding; 4 | using Microsoft.AspNetCore.Mvc.Razor; 5 | using Microsoft.AspNetCore.Mvc.Rendering; 6 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | namespace HotelProject.Services 10 | { 11 | public class ViewRender : IViewRender 12 | { 13 | private IRazorViewEngine _viewEngine; 14 | private ITempDataProvider _tempDataProvider; 15 | private IServiceProvider _serviceProvider; 16 | 17 | public ViewRender( 18 | IRazorViewEngine viewEngine, 19 | ITempDataProvider tempDataProvider, 20 | IServiceProvider serviceProvider) 21 | { 22 | _viewEngine = viewEngine; 23 | _tempDataProvider = tempDataProvider; 24 | _serviceProvider = serviceProvider; 25 | } 26 | 27 | public string Render(string name, TModel model) 28 | { 29 | var actionContext = GetActionContext(); 30 | 31 | var viewEngineResult = _viewEngine.FindView(actionContext, name, false); 32 | 33 | if (!viewEngineResult.Success) 34 | { 35 | throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", name)); 36 | } 37 | 38 | var view = viewEngineResult.View; 39 | 40 | using (var output = new StringWriter()) 41 | { 42 | var viewContext = new ViewContext( 43 | actionContext, 44 | view, 45 | new ViewDataDictionary( 46 | metadataProvider: new EmptyModelMetadataProvider(), 47 | modelState: new ModelStateDictionary()) 48 | { 49 | Model = model 50 | }, 51 | new TempDataDictionary( 52 | actionContext.HttpContext, 53 | _tempDataProvider), 54 | output, 55 | new HtmlHelperOptions()); 56 | 57 | view.RenderAsync(viewContext).GetAwaiter().GetResult(); 58 | 59 | return output.ToString(); 60 | } 61 | } 62 | 63 | private ActionContext GetActionContext() 64 | { 65 | var httpContext = new DefaultHttpContext(); 66 | httpContext.RequestServices = _serviceProvider; 67 | return new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /DataAccess/Repositories/Repository.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.Specification; 2 | using Ardalis.Specification.EntityFrameworkCore; 3 | using DataAccess.Data; 4 | using Microsoft.EntityFrameworkCore; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Linq.Expressions; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DataAccess.Repositories 13 | { 14 | public class Repository : IRepository where TEntity : class 15 | { 16 | internal ShopDbContext context; 17 | internal DbSet dbSet; 18 | 19 | public Repository(ShopDbContext context) 20 | { 21 | this.context = context; 22 | this.dbSet = context.Set(); 23 | } 24 | 25 | public IEnumerable GetAll() 26 | { 27 | return dbSet.ToList(); 28 | } 29 | 30 | public virtual TEntity GetById(object id) 31 | { 32 | return dbSet.Find(id); 33 | } 34 | 35 | public virtual void Insert(TEntity entity) 36 | { 37 | dbSet.Add(entity); 38 | } 39 | 40 | public virtual void Delete(object id) 41 | { 42 | TEntity entityToDelete = dbSet.Find(id); 43 | Delete(entityToDelete); 44 | } 45 | 46 | public virtual void Delete(TEntity entityToDelete) 47 | { 48 | if (context.Entry(entityToDelete).State == EntityState.Detached) 49 | { 50 | dbSet.Attach(entityToDelete); 51 | } 52 | dbSet.Remove(entityToDelete); 53 | } 54 | 55 | public virtual void Update(TEntity entityToUpdate) 56 | { 57 | dbSet.Attach(entityToUpdate); 58 | context.Entry(entityToUpdate).State = EntityState.Modified; 59 | } 60 | 61 | public void Save() 62 | { 63 | context.SaveChanges(); 64 | } 65 | 66 | // working with specifications 67 | public async Task> GetListBySpec(ISpecification specification) 68 | { 69 | return await ApplySpecification(specification).ToListAsync(); 70 | } 71 | 72 | public async Task GetItemBySpec(ISpecification specification) 73 | { 74 | return await ApplySpecification(specification).FirstOrDefaultAsync(); 75 | } 76 | 77 | private IQueryable ApplySpecification(ISpecification specification) 78 | { 79 | var evaluator = new SpecificationEvaluator(); 80 | return evaluator.GetQuery(dbSet, specification); 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /BusinessLogic/Errors.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace BusinessLogic { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Errors { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Errors() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BusinessLogic.Errors", typeof(Errors).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Id must be positive. 65 | /// 66 | internal static string IdMustPositive { 67 | get { 68 | return ResourceManager.GetString("IdMustPositive", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to Invalid token. 74 | /// 75 | internal static string InvalidToken { 76 | get { 77 | return ResourceManager.GetString("InvalidToken", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Room not found. 83 | /// 84 | internal static string RoomNotFound { 85 | get { 86 | return ResourceManager.GetString("RoomNotFound", resourceCulture); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /BusinessLogic/Services/RoomsService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BusinessLogic.DTOs; 3 | using BusinessLogic.Interfaces; 4 | using DataAccess.Data; 5 | using DataAccess.Repositories; 6 | using BusinessLogic.Specifications; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Net; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using static System.Runtime.InteropServices.JavaScript.JSType; 14 | 15 | namespace BusinessLogic.Services 16 | { 17 | public class RoomsService : IRoomsService 18 | { 19 | private readonly IMapper mapper; 20 | private readonly IRepository roomsR; 21 | private readonly IRepository categoriesR; 22 | //private readonly ShopDbContext context; 23 | 24 | public RoomsService(IMapper mapper, 25 | IRepository roomsR, 26 | IRepository categoriesR) 27 | { 28 | this.mapper = mapper; 29 | this.roomsR = roomsR; 30 | this.categoriesR = categoriesR; 31 | } 32 | 33 | public void Create(CreateRoomModel room) 34 | { 35 | roomsR.Insert(mapper.Map(room)); 36 | roomsR.Save(); 37 | } 38 | 39 | public void Delete(int id) 40 | { 41 | if (id < 0) throw new HttpException(Errors.IdMustPositive, HttpStatusCode.BadRequest); 42 | 43 | // delete product by id 44 | var product = roomsR.GetById(id); 45 | 46 | if (product == null) throw new HttpException(Errors.RoomNotFound, HttpStatusCode.NotFound); 47 | 48 | roomsR.Delete(product); 49 | roomsR.Save(); 50 | } 51 | 52 | public void Edit(RoomDto room) 53 | { 54 | roomsR.Update(mapper.Map(room)); 55 | roomsR.Save(); 56 | } 57 | 58 | public async Task Get(int id) 59 | { 60 | if (id < 0) throw new HttpException(Errors.IdMustPositive, HttpStatusCode.BadRequest); 61 | 62 | var item = await roomsR.GetItemBySpec(new RoomSpecs.ById(id)); 63 | if (item == null) throw new HttpException(Errors.RoomNotFound, HttpStatusCode.NotFound); 64 | 65 | // load related entity 66 | //context.Entry(item).Reference(x => x.Category).Load(); 67 | 68 | // convert entity type to DTO 69 | // 1 - using manually (handmade) 70 | //var dto = new ProductDto() 71 | //{ 72 | // Id = product.Id, 73 | // CategoryId = product.CategoryId, 74 | // Description = product.Description, 75 | // Discount = product.Discount, 76 | // ImageUrl = product.ImageUrl, 77 | // InStock = product.InStock, 78 | // Name = product.Name, 79 | // Price = product.Price, 80 | // CategoryName = product.Category.Name 81 | //}; 82 | // 2 - using AutoMapper 83 | var dto = mapper.Map(item); 84 | 85 | return dto; 86 | } 87 | 88 | public async Task> Get(IEnumerable ids) 89 | { 90 | //return mapper.Map>(context.Products 91 | // .Include(x => x.Category) 92 | // .Where(x => ids.Contains(x.Id)) 93 | // .ToList()); 94 | return mapper.Map>(await roomsR.GetListBySpec(new RoomSpecs.ByIds(ids))); 95 | } 96 | 97 | public async Task> GetAll() 98 | { 99 | return mapper.Map>(await roomsR.GetListBySpec(new RoomSpecs.All())); 100 | } 101 | 102 | public IEnumerable GetAllCategories() 103 | { 104 | return mapper.Map>(categoriesR.GetAll()); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /BusinessLogic/Services/JwtService.cs: -------------------------------------------------------------------------------- 1 | using BusinessLogic.Helpers; 2 | using BusinessLogic.Interfaces; 3 | using DataAccess.Data; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.IdentityModel.Tokens; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IdentityModel.Tokens.Jwt; 10 | using System.Linq; 11 | using System.Net; 12 | using System.Security.Claims; 13 | using System.Security.Cryptography; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | using static System.Runtime.InteropServices.JavaScript.JSType; 17 | 18 | namespace BusinessLogic.Services 19 | { 20 | internal class JwtService : IJwtService 21 | { 22 | private readonly IConfiguration configuration; 23 | private readonly UserManager userManager; 24 | private readonly JwtOptions jwtOptions; 25 | 26 | public JwtService(IConfiguration configuration, UserManager userManager) 27 | { 28 | this.configuration = configuration; 29 | this.userManager = userManager; 30 | this.jwtOptions = configuration.GetSection(nameof(JwtOptions)).Get()!; 31 | } 32 | 33 | public string CreateToken(IEnumerable claims) 34 | { 35 | // TODO: make separate method 36 | var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.Key)); 37 | var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); 38 | 39 | var token = new JwtSecurityToken( 40 | issuer: jwtOptions.Issuer, 41 | claims: claims, 42 | expires: DateTime.UtcNow.AddMinutes(jwtOptions.AcceeTokenLifetimeInMinutes), 43 | signingCredentials: credentials); 44 | 45 | return new JwtSecurityTokenHandler().WriteToken(token); 46 | } 47 | 48 | public IEnumerable GetClaims(User user) 49 | { 50 | var claims = new List 51 | { 52 | new Claim(ClaimTypes.NameIdentifier, user.Id), 53 | new Claim(ClaimTypes.Email, user.Email), 54 | new Claim(ClaimTypes.DateOfBirth, user.Birthdate.ToString()), 55 | new Claim("ClientType", user.ClientType.ToString()) 56 | }; 57 | 58 | var roles = userManager.GetRolesAsync(user).Result; 59 | claims.AddRange(roles.Select(role => new Claim(ClaimsIdentity.DefaultRoleClaimType, role))); 60 | 61 | return claims; 62 | } 63 | 64 | public string CreateRefreshToken() 65 | { 66 | var randomNumber = new byte[64]; 67 | using var rng = RandomNumberGenerator.Create(); 68 | rng.GetBytes(randomNumber); 69 | return Convert.ToBase64String(randomNumber); 70 | } 71 | 72 | public IEnumerable GetClaimsFromExpiredToken(string token) 73 | { 74 | var tokenValidationParameters = new TokenValidationParameters 75 | { 76 | ValidateIssuer = true, 77 | ValidateAudience = false, 78 | ValidateLifetime = false, 79 | ValidateIssuerSigningKey = true, 80 | ValidIssuer = jwtOptions.Issuer, 81 | IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.Key)), 82 | }; 83 | 84 | var tokenHandler = new JwtSecurityTokenHandler(); 85 | JwtSecurityToken jwtSecurityToken; 86 | 87 | tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken securityToken); 88 | jwtSecurityToken = securityToken as JwtSecurityToken; 89 | 90 | if (jwtSecurityToken == null || 91 | !jwtSecurityToken.Header.Alg 92 | .Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase)) 93 | { 94 | throw new HttpException(Errors.InvalidToken, HttpStatusCode.BadRequest); 95 | } 96 | 97 | return jwtSecurityToken.Claims; 98 | } 99 | 100 | public DateTime GetLastValidRefreshTokenDate() 101 | { 102 | return DateTime.UtcNow.AddDays(-jwtOptions.RefreshTokenLifetimeInDays); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /BusinessLogic/Services/AccountsService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BusinessLogic.DTOs; 3 | using BusinessLogic.Interfaces; 4 | using DataAccess.Data; 5 | using Microsoft.AspNetCore.Identity; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Net.Mail; 10 | using System.Net; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using static System.Runtime.InteropServices.JavaScript.JSType; 14 | using DataAccess.Repositories; 15 | using BusinessLogic.Entities; 16 | using BusinessLogic.DTOs; 17 | using BusinessLogic.Specifications; 18 | 19 | namespace BusinessLogic.Services 20 | { 21 | public class AccountsService : IAccountsService 22 | { 23 | private readonly UserManager userManager; 24 | private readonly SignInManager signInManager; 25 | private readonly IMapper mapper; 26 | private readonly IJwtService jwtService; 27 | private readonly IRepository refreshTokenR; 28 | 29 | public AccountsService(UserManager userManager, 30 | SignInManager signInManager, 31 | IMapper mapper, 32 | IJwtService jwtService, 33 | IRepository refreshTokenR) 34 | { 35 | this.userManager = userManager; 36 | this.signInManager = signInManager; 37 | this.mapper = mapper; 38 | this.jwtService = jwtService; 39 | this.refreshTokenR = refreshTokenR; 40 | } 41 | 42 | public async Task Register(RegisterModel model) 43 | { 44 | // TODO: validation 45 | 46 | // check user 47 | var user = await userManager.FindByEmailAsync(model.Email); 48 | 49 | if (user != null) 50 | throw new HttpException("Email is already exists.", HttpStatusCode.BadRequest); 51 | 52 | // create user 53 | var newUser = mapper.Map(model); 54 | var result = await userManager.CreateAsync(newUser, model.Password); 55 | 56 | if (!result.Succeeded) 57 | throw new HttpException(string.Join(" ", result.Errors.Select(x => x.Description)), HttpStatusCode.BadRequest); 58 | } 59 | 60 | public async Task Login(LoginModel model) 61 | { 62 | var user = await userManager.FindByEmailAsync(model.Email); 63 | 64 | if (user == null || !await userManager.CheckPasswordAsync(user, model.Password)) 65 | throw new HttpException("Invalid user login or password.", HttpStatusCode.BadRequest); 66 | 67 | //await signInManager.SignInAsync(user, true); 68 | 69 | // generate token 70 | return new LoginResponseDto 71 | { 72 | AccessToken = jwtService.CreateToken(jwtService.GetClaims(user)), 73 | RefreshToken = CreateRefreshToken(user.Id).Token 74 | }; 75 | } 76 | 77 | private RefreshToken CreateRefreshToken(string userId) 78 | { 79 | var refeshToken = jwtService.CreateRefreshToken(); 80 | 81 | var refreshTokenEntity = new RefreshToken 82 | { 83 | Token = refeshToken, 84 | UserId = userId, 85 | CreationDate = DateTime.UtcNow // Now vs UtcNow 86 | }; 87 | 88 | refreshTokenR.Insert(refreshTokenEntity); 89 | refreshTokenR.Save(); 90 | 91 | return refreshTokenEntity; 92 | } 93 | 94 | public async Task RefreshTokens(UserTokens userTokens) 95 | { 96 | var refrestToken = await refreshTokenR.GetItemBySpec(new RefreshTokenSpecs.ByToken(userTokens.RefreshToken)); 97 | 98 | if (refrestToken == null) 99 | throw new HttpException(Errors.InvalidToken, HttpStatusCode.BadRequest); 100 | 101 | var claims = jwtService.GetClaimsFromExpiredToken(userTokens.AccessToken); 102 | var newAccessToken = jwtService.CreateToken(claims); 103 | var newRefreshToken = jwtService.CreateRefreshToken(); 104 | 105 | refrestToken.Token = newRefreshToken; 106 | 107 | // TODO: update creation time 108 | refreshTokenR.Update(refrestToken); 109 | refreshTokenR.Save(); 110 | 111 | var tokens = new UserTokens() 112 | { 113 | AccessToken = newAccessToken, 114 | RefreshToken = newRefreshToken 115 | }; 116 | 117 | return tokens; 118 | } 119 | 120 | public async Task Logout(string refreshToken) 121 | { 122 | //await signInManager.SignOutAsync(); 123 | 124 | var refrestTokenEntity = await refreshTokenR.GetItemBySpec(new RefreshTokenSpecs.ByToken(refreshToken)); 125 | 126 | if (refrestTokenEntity == null) 127 | throw new HttpException(Errors.InvalidToken, HttpStatusCode.BadRequest); 128 | 129 | refreshTokenR.Delete(refrestTokenEntity); 130 | refreshTokenR.Save(); 131 | } 132 | 133 | public async Task RemoveExpiredRefreshTokens() 134 | { 135 | var lastDate = jwtService.GetLastValidRefreshTokenDate(); 136 | var expiredTokens = await refreshTokenR.GetListBySpec(new RefreshTokenSpecs.CreatedBy(lastDate)); 137 | 138 | foreach (var i in expiredTokens) 139 | { 140 | refreshTokenR.Delete(i); 141 | } 142 | refreshTokenR.Save(); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /BusinessLogic/Errors.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Id must be positive 122 | 123 | 124 | Invalid token 125 | 126 | 127 | Room not found 128 | 129 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /DataAccess/Migrations/20240308204547_Initial.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace DataAccess.Migrations 7 | { 8 | /// 9 | public partial class Initial : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "AspNetRoles", 16 | columns: table => new 17 | { 18 | Id = table.Column(type: "nvarchar(450)", nullable: false), 19 | Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), 20 | NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), 21 | ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_AspNetRoles", x => x.Id); 26 | }); 27 | 28 | migrationBuilder.CreateTable( 29 | name: "AspNetUsers", 30 | columns: table => new 31 | { 32 | Id = table.Column(type: "nvarchar(450)", nullable: false), 33 | Name = table.Column(type: "nvarchar(max)", nullable: false), 34 | Surname = table.Column(type: "nvarchar(max)", nullable: false), 35 | Birthdate = table.Column(type: "datetime2", nullable: false), 36 | ClientType = table.Column(type: "int", nullable: false), 37 | UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), 38 | NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), 39 | Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), 40 | NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), 41 | EmailConfirmed = table.Column(type: "bit", nullable: false), 42 | PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), 43 | SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), 44 | ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), 45 | PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), 46 | PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), 47 | TwoFactorEnabled = table.Column(type: "bit", nullable: false), 48 | LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), 49 | LockoutEnabled = table.Column(type: "bit", nullable: false), 50 | AccessFailedCount = table.Column(type: "int", nullable: false) 51 | }, 52 | constraints: table => 53 | { 54 | table.PrimaryKey("PK_AspNetUsers", x => x.Id); 55 | }); 56 | 57 | migrationBuilder.CreateTable( 58 | name: "Category", 59 | columns: table => new 60 | { 61 | Id = table.Column(type: "int", nullable: false) 62 | .Annotation("SqlServer:Identity", "1, 1"), 63 | Name = table.Column(type: "nvarchar(max)", nullable: false) 64 | }, 65 | constraints: table => 66 | { 67 | table.PrimaryKey("PK_Category", x => x.Id); 68 | }); 69 | 70 | migrationBuilder.CreateTable( 71 | name: "AspNetRoleClaims", 72 | columns: table => new 73 | { 74 | Id = table.Column(type: "int", nullable: false) 75 | .Annotation("SqlServer:Identity", "1, 1"), 76 | RoleId = table.Column(type: "nvarchar(450)", nullable: false), 77 | ClaimType = table.Column(type: "nvarchar(max)", nullable: true), 78 | ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) 79 | }, 80 | constraints: table => 81 | { 82 | table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); 83 | table.ForeignKey( 84 | name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", 85 | column: x => x.RoleId, 86 | principalTable: "AspNetRoles", 87 | principalColumn: "Id", 88 | onDelete: ReferentialAction.Cascade); 89 | }); 90 | 91 | migrationBuilder.CreateTable( 92 | name: "AspNetUserClaims", 93 | columns: table => new 94 | { 95 | Id = table.Column(type: "int", nullable: false) 96 | .Annotation("SqlServer:Identity", "1, 1"), 97 | UserId = table.Column(type: "nvarchar(450)", nullable: false), 98 | ClaimType = table.Column(type: "nvarchar(max)", nullable: true), 99 | ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) 100 | }, 101 | constraints: table => 102 | { 103 | table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); 104 | table.ForeignKey( 105 | name: "FK_AspNetUserClaims_AspNetUsers_UserId", 106 | column: x => x.UserId, 107 | principalTable: "AspNetUsers", 108 | principalColumn: "Id", 109 | onDelete: ReferentialAction.Cascade); 110 | }); 111 | 112 | migrationBuilder.CreateTable( 113 | name: "AspNetUserLogins", 114 | columns: table => new 115 | { 116 | LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), 117 | ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), 118 | ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), 119 | UserId = table.Column(type: "nvarchar(450)", nullable: false) 120 | }, 121 | constraints: table => 122 | { 123 | table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); 124 | table.ForeignKey( 125 | name: "FK_AspNetUserLogins_AspNetUsers_UserId", 126 | column: x => x.UserId, 127 | principalTable: "AspNetUsers", 128 | principalColumn: "Id", 129 | onDelete: ReferentialAction.Cascade); 130 | }); 131 | 132 | migrationBuilder.CreateTable( 133 | name: "AspNetUserRoles", 134 | columns: table => new 135 | { 136 | UserId = table.Column(type: "nvarchar(450)", nullable: false), 137 | RoleId = table.Column(type: "nvarchar(450)", nullable: false) 138 | }, 139 | constraints: table => 140 | { 141 | table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); 142 | table.ForeignKey( 143 | name: "FK_AspNetUserRoles_AspNetRoles_RoleId", 144 | column: x => x.RoleId, 145 | principalTable: "AspNetRoles", 146 | principalColumn: "Id", 147 | onDelete: ReferentialAction.Cascade); 148 | table.ForeignKey( 149 | name: "FK_AspNetUserRoles_AspNetUsers_UserId", 150 | column: x => x.UserId, 151 | principalTable: "AspNetUsers", 152 | principalColumn: "Id", 153 | onDelete: ReferentialAction.Cascade); 154 | }); 155 | 156 | migrationBuilder.CreateTable( 157 | name: "AspNetUserTokens", 158 | columns: table => new 159 | { 160 | UserId = table.Column(type: "nvarchar(450)", nullable: false), 161 | LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), 162 | Name = table.Column(type: "nvarchar(450)", nullable: false), 163 | Value = table.Column(type: "nvarchar(max)", nullable: true) 164 | }, 165 | constraints: table => 166 | { 167 | table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); 168 | table.ForeignKey( 169 | name: "FK_AspNetUserTokens_AspNetUsers_UserId", 170 | column: x => x.UserId, 171 | principalTable: "AspNetUsers", 172 | principalColumn: "Id", 173 | onDelete: ReferentialAction.Cascade); 174 | }); 175 | 176 | migrationBuilder.CreateTable( 177 | name: "RefreshTokens", 178 | columns: table => new 179 | { 180 | Id = table.Column(type: "int", nullable: false) 181 | .Annotation("SqlServer:Identity", "1, 1"), 182 | Token = table.Column(type: "nvarchar(max)", nullable: false), 183 | CreationDate = table.Column(type: "datetime2", nullable: false), 184 | UserId = table.Column(type: "nvarchar(450)", nullable: false) 185 | }, 186 | constraints: table => 187 | { 188 | table.PrimaryKey("PK_RefreshTokens", x => x.Id); 189 | table.ForeignKey( 190 | name: "FK_RefreshTokens_AspNetUsers_UserId", 191 | column: x => x.UserId, 192 | principalTable: "AspNetUsers", 193 | principalColumn: "Id", 194 | onDelete: ReferentialAction.Cascade); 195 | }); 196 | 197 | migrationBuilder.CreateTable( 198 | name: "Room", 199 | columns: table => new 200 | { 201 | Id = table.Column(type: "int", nullable: false) 202 | .Annotation("SqlServer:Identity", "1, 1"), 203 | Price = table.Column(type: "decimal(18,2)", nullable: false), 204 | CategoryId = table.Column(type: "int", nullable: false), 205 | ImageUrl = table.Column(type: "nvarchar(max)", nullable: false) 206 | }, 207 | constraints: table => 208 | { 209 | table.PrimaryKey("PK_Room", x => x.Id); 210 | table.ForeignKey( 211 | name: "FK_Room_Category_CategoryId", 212 | column: x => x.CategoryId, 213 | principalTable: "Category", 214 | principalColumn: "Id", 215 | onDelete: ReferentialAction.Cascade); 216 | }); 217 | 218 | migrationBuilder.CreateTable( 219 | name: "Reservation", 220 | columns: table => new 221 | { 222 | Id = table.Column(type: "int", nullable: false) 223 | .Annotation("SqlServer:Identity", "1, 1"), 224 | UserId = table.Column(type: "nvarchar(450)", nullable: false), 225 | CheckInDate = table.Column(type: "datetime2", nullable: false), 226 | CheckOutDate = table.Column(type: "datetime2", nullable: false), 227 | RoomId = table.Column(type: "int", nullable: false) 228 | }, 229 | constraints: table => 230 | { 231 | table.PrimaryKey("PK_Reservation", x => x.Id); 232 | table.ForeignKey( 233 | name: "FK_Reservation_AspNetUsers_UserId", 234 | column: x => x.UserId, 235 | principalTable: "AspNetUsers", 236 | principalColumn: "Id", 237 | onDelete: ReferentialAction.Cascade); 238 | table.ForeignKey( 239 | name: "FK_Reservation_Room_RoomId", 240 | column: x => x.RoomId, 241 | principalTable: "Room", 242 | principalColumn: "Id", 243 | onDelete: ReferentialAction.Cascade); 244 | }); 245 | 246 | migrationBuilder.CreateIndex( 247 | name: "IX_AspNetRoleClaims_RoleId", 248 | table: "AspNetRoleClaims", 249 | column: "RoleId"); 250 | 251 | migrationBuilder.CreateIndex( 252 | name: "RoleNameIndex", 253 | table: "AspNetRoles", 254 | column: "NormalizedName", 255 | unique: true, 256 | filter: "[NormalizedName] IS NOT NULL"); 257 | 258 | migrationBuilder.CreateIndex( 259 | name: "IX_AspNetUserClaims_UserId", 260 | table: "AspNetUserClaims", 261 | column: "UserId"); 262 | 263 | migrationBuilder.CreateIndex( 264 | name: "IX_AspNetUserLogins_UserId", 265 | table: "AspNetUserLogins", 266 | column: "UserId"); 267 | 268 | migrationBuilder.CreateIndex( 269 | name: "IX_AspNetUserRoles_RoleId", 270 | table: "AspNetUserRoles", 271 | column: "RoleId"); 272 | 273 | migrationBuilder.CreateIndex( 274 | name: "EmailIndex", 275 | table: "AspNetUsers", 276 | column: "NormalizedEmail"); 277 | 278 | migrationBuilder.CreateIndex( 279 | name: "UserNameIndex", 280 | table: "AspNetUsers", 281 | column: "NormalizedUserName", 282 | unique: true, 283 | filter: "[NormalizedUserName] IS NOT NULL"); 284 | 285 | migrationBuilder.CreateIndex( 286 | name: "IX_RefreshTokens_UserId", 287 | table: "RefreshTokens", 288 | column: "UserId"); 289 | 290 | migrationBuilder.CreateIndex( 291 | name: "IX_Reservation_RoomId", 292 | table: "Reservation", 293 | column: "RoomId"); 294 | 295 | migrationBuilder.CreateIndex( 296 | name: "IX_Reservation_UserId", 297 | table: "Reservation", 298 | column: "UserId"); 299 | 300 | migrationBuilder.CreateIndex( 301 | name: "IX_Room_CategoryId", 302 | table: "Room", 303 | column: "CategoryId"); 304 | } 305 | 306 | /// 307 | protected override void Down(MigrationBuilder migrationBuilder) 308 | { 309 | migrationBuilder.DropTable( 310 | name: "AspNetRoleClaims"); 311 | 312 | migrationBuilder.DropTable( 313 | name: "AspNetUserClaims"); 314 | 315 | migrationBuilder.DropTable( 316 | name: "AspNetUserLogins"); 317 | 318 | migrationBuilder.DropTable( 319 | name: "AspNetUserRoles"); 320 | 321 | migrationBuilder.DropTable( 322 | name: "AspNetUserTokens"); 323 | 324 | migrationBuilder.DropTable( 325 | name: "RefreshTokens"); 326 | 327 | migrationBuilder.DropTable( 328 | name: "Reservation"); 329 | 330 | migrationBuilder.DropTable( 331 | name: "AspNetRoles"); 332 | 333 | migrationBuilder.DropTable( 334 | name: "AspNetUsers"); 335 | 336 | migrationBuilder.DropTable( 337 | name: "Room"); 338 | 339 | migrationBuilder.DropTable( 340 | name: "Category"); 341 | } 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /DataAccess/Migrations/ShopDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using DataAccess.Data; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | #nullable disable 10 | 11 | namespace DataAccess.Migrations 12 | { 13 | [DbContext(typeof(ShopDbContext))] 14 | partial class ShopDbContextModelSnapshot : ModelSnapshot 15 | { 16 | protected override void BuildModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("ProductVersion", "7.0.16") 21 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 22 | 23 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); 24 | 25 | modelBuilder.Entity("BusinessLogic.Entities.RefreshToken", b => 26 | { 27 | b.Property("Id") 28 | .ValueGeneratedOnAdd() 29 | .HasColumnType("int"); 30 | 31 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 32 | 33 | b.Property("CreationDate") 34 | .HasColumnType("datetime2"); 35 | 36 | b.Property("Token") 37 | .IsRequired() 38 | .HasColumnType("nvarchar(max)"); 39 | 40 | b.Property("UserId") 41 | .IsRequired() 42 | .HasColumnType("nvarchar(450)"); 43 | 44 | b.HasKey("Id"); 45 | 46 | b.HasIndex("UserId"); 47 | 48 | b.ToTable("RefreshTokens", (string)null); 49 | }); 50 | 51 | modelBuilder.Entity("DataAccess.Data.Category", b => 52 | { 53 | b.Property("Id") 54 | .ValueGeneratedOnAdd() 55 | .HasColumnType("int"); 56 | 57 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 58 | 59 | b.Property("Name") 60 | .IsRequired() 61 | .HasColumnType("nvarchar(max)"); 62 | 63 | b.HasKey("Id"); 64 | 65 | b.ToTable("Category"); 66 | }); 67 | 68 | modelBuilder.Entity("DataAccess.Data.Reservation", b => 69 | { 70 | b.Property("Id") 71 | .ValueGeneratedOnAdd() 72 | .HasColumnType("int"); 73 | 74 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 75 | 76 | b.Property("CheckInDate") 77 | .HasColumnType("datetime2"); 78 | 79 | b.Property("CheckOutDate") 80 | .HasColumnType("datetime2"); 81 | 82 | b.Property("RoomId") 83 | .HasColumnType("int"); 84 | 85 | b.Property("UserId") 86 | .IsRequired() 87 | .HasColumnType("nvarchar(450)"); 88 | 89 | b.HasKey("Id"); 90 | 91 | b.HasIndex("RoomId"); 92 | 93 | b.HasIndex("UserId"); 94 | 95 | b.ToTable("Reservation"); 96 | }); 97 | 98 | modelBuilder.Entity("DataAccess.Data.Room", b => 99 | { 100 | b.Property("Id") 101 | .ValueGeneratedOnAdd() 102 | .HasColumnType("int"); 103 | 104 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 105 | 106 | b.Property("CategoryId") 107 | .HasColumnType("int"); 108 | 109 | b.Property("ImageUrl") 110 | .IsRequired() 111 | .HasColumnType("nvarchar(max)"); 112 | 113 | b.Property("Price") 114 | .HasColumnType("decimal(18,2)"); 115 | 116 | b.HasKey("Id"); 117 | 118 | b.HasIndex("CategoryId"); 119 | 120 | b.ToTable("Room"); 121 | }); 122 | 123 | modelBuilder.Entity("DataAccess.Data.User", b => 124 | { 125 | b.Property("Id") 126 | .HasColumnType("nvarchar(450)"); 127 | 128 | b.Property("AccessFailedCount") 129 | .HasColumnType("int"); 130 | 131 | b.Property("Birthdate") 132 | .HasColumnType("datetime2"); 133 | 134 | b.Property("ClientType") 135 | .HasColumnType("int"); 136 | 137 | b.Property("ConcurrencyStamp") 138 | .IsConcurrencyToken() 139 | .HasColumnType("nvarchar(max)"); 140 | 141 | b.Property("Email") 142 | .HasMaxLength(256) 143 | .HasColumnType("nvarchar(256)"); 144 | 145 | b.Property("EmailConfirmed") 146 | .HasColumnType("bit"); 147 | 148 | b.Property("LockoutEnabled") 149 | .HasColumnType("bit"); 150 | 151 | b.Property("LockoutEnd") 152 | .HasColumnType("datetimeoffset"); 153 | 154 | b.Property("Name") 155 | .IsRequired() 156 | .HasColumnType("nvarchar(max)"); 157 | 158 | b.Property("NormalizedEmail") 159 | .HasMaxLength(256) 160 | .HasColumnType("nvarchar(256)"); 161 | 162 | b.Property("NormalizedUserName") 163 | .HasMaxLength(256) 164 | .HasColumnType("nvarchar(256)"); 165 | 166 | b.Property("PasswordHash") 167 | .HasColumnType("nvarchar(max)"); 168 | 169 | b.Property("PhoneNumber") 170 | .HasColumnType("nvarchar(max)"); 171 | 172 | b.Property("PhoneNumberConfirmed") 173 | .HasColumnType("bit"); 174 | 175 | b.Property("SecurityStamp") 176 | .HasColumnType("nvarchar(max)"); 177 | 178 | b.Property("Surname") 179 | .IsRequired() 180 | .HasColumnType("nvarchar(max)"); 181 | 182 | b.Property("TwoFactorEnabled") 183 | .HasColumnType("bit"); 184 | 185 | b.Property("UserName") 186 | .HasMaxLength(256) 187 | .HasColumnType("nvarchar(256)"); 188 | 189 | b.HasKey("Id"); 190 | 191 | b.HasIndex("NormalizedEmail") 192 | .HasDatabaseName("EmailIndex"); 193 | 194 | b.HasIndex("NormalizedUserName") 195 | .IsUnique() 196 | .HasDatabaseName("UserNameIndex") 197 | .HasFilter("[NormalizedUserName] IS NOT NULL"); 198 | 199 | b.ToTable("AspNetUsers", (string)null); 200 | }); 201 | 202 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => 203 | { 204 | b.Property("Id") 205 | .HasColumnType("nvarchar(450)"); 206 | 207 | b.Property("ConcurrencyStamp") 208 | .IsConcurrencyToken() 209 | .HasColumnType("nvarchar(max)"); 210 | 211 | b.Property("Name") 212 | .HasMaxLength(256) 213 | .HasColumnType("nvarchar(256)"); 214 | 215 | b.Property("NormalizedName") 216 | .HasMaxLength(256) 217 | .HasColumnType("nvarchar(256)"); 218 | 219 | b.HasKey("Id"); 220 | 221 | b.HasIndex("NormalizedName") 222 | .IsUnique() 223 | .HasDatabaseName("RoleNameIndex") 224 | .HasFilter("[NormalizedName] IS NOT NULL"); 225 | 226 | b.ToTable("AspNetRoles", (string)null); 227 | }); 228 | 229 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 230 | { 231 | b.Property("Id") 232 | .ValueGeneratedOnAdd() 233 | .HasColumnType("int"); 234 | 235 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 236 | 237 | b.Property("ClaimType") 238 | .HasColumnType("nvarchar(max)"); 239 | 240 | b.Property("ClaimValue") 241 | .HasColumnType("nvarchar(max)"); 242 | 243 | b.Property("RoleId") 244 | .IsRequired() 245 | .HasColumnType("nvarchar(450)"); 246 | 247 | b.HasKey("Id"); 248 | 249 | b.HasIndex("RoleId"); 250 | 251 | b.ToTable("AspNetRoleClaims", (string)null); 252 | }); 253 | 254 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 255 | { 256 | b.Property("Id") 257 | .ValueGeneratedOnAdd() 258 | .HasColumnType("int"); 259 | 260 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 261 | 262 | b.Property("ClaimType") 263 | .HasColumnType("nvarchar(max)"); 264 | 265 | b.Property("ClaimValue") 266 | .HasColumnType("nvarchar(max)"); 267 | 268 | b.Property("UserId") 269 | .IsRequired() 270 | .HasColumnType("nvarchar(450)"); 271 | 272 | b.HasKey("Id"); 273 | 274 | b.HasIndex("UserId"); 275 | 276 | b.ToTable("AspNetUserClaims", (string)null); 277 | }); 278 | 279 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 280 | { 281 | b.Property("LoginProvider") 282 | .HasColumnType("nvarchar(450)"); 283 | 284 | b.Property("ProviderKey") 285 | .HasColumnType("nvarchar(450)"); 286 | 287 | b.Property("ProviderDisplayName") 288 | .HasColumnType("nvarchar(max)"); 289 | 290 | b.Property("UserId") 291 | .IsRequired() 292 | .HasColumnType("nvarchar(450)"); 293 | 294 | b.HasKey("LoginProvider", "ProviderKey"); 295 | 296 | b.HasIndex("UserId"); 297 | 298 | b.ToTable("AspNetUserLogins", (string)null); 299 | }); 300 | 301 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 302 | { 303 | b.Property("UserId") 304 | .HasColumnType("nvarchar(450)"); 305 | 306 | b.Property("RoleId") 307 | .HasColumnType("nvarchar(450)"); 308 | 309 | b.HasKey("UserId", "RoleId"); 310 | 311 | b.HasIndex("RoleId"); 312 | 313 | b.ToTable("AspNetUserRoles", (string)null); 314 | }); 315 | 316 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 317 | { 318 | b.Property("UserId") 319 | .HasColumnType("nvarchar(450)"); 320 | 321 | b.Property("LoginProvider") 322 | .HasColumnType("nvarchar(450)"); 323 | 324 | b.Property("Name") 325 | .HasColumnType("nvarchar(450)"); 326 | 327 | b.Property("Value") 328 | .HasColumnType("nvarchar(max)"); 329 | 330 | b.HasKey("UserId", "LoginProvider", "Name"); 331 | 332 | b.ToTable("AspNetUserTokens", (string)null); 333 | }); 334 | 335 | modelBuilder.Entity("BusinessLogic.Entities.RefreshToken", b => 336 | { 337 | b.HasOne("DataAccess.Data.User", "User") 338 | .WithMany("RefreshTokens") 339 | .HasForeignKey("UserId") 340 | .OnDelete(DeleteBehavior.Cascade) 341 | .IsRequired(); 342 | 343 | b.Navigation("User"); 344 | }); 345 | 346 | modelBuilder.Entity("DataAccess.Data.Reservation", b => 347 | { 348 | b.HasOne("DataAccess.Data.Room", "Room") 349 | .WithMany("Reservations") 350 | .HasForeignKey("RoomId") 351 | .OnDelete(DeleteBehavior.Cascade) 352 | .IsRequired(); 353 | 354 | b.HasOne("DataAccess.Data.User", "User") 355 | .WithMany("Reservations") 356 | .HasForeignKey("UserId") 357 | .OnDelete(DeleteBehavior.Cascade) 358 | .IsRequired(); 359 | 360 | b.Navigation("Room"); 361 | 362 | b.Navigation("User"); 363 | }); 364 | 365 | modelBuilder.Entity("DataAccess.Data.Room", b => 366 | { 367 | b.HasOne("DataAccess.Data.Category", "Category") 368 | .WithMany("Rooms") 369 | .HasForeignKey("CategoryId") 370 | .OnDelete(DeleteBehavior.Cascade) 371 | .IsRequired(); 372 | 373 | b.Navigation("Category"); 374 | }); 375 | 376 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 377 | { 378 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) 379 | .WithMany() 380 | .HasForeignKey("RoleId") 381 | .OnDelete(DeleteBehavior.Cascade) 382 | .IsRequired(); 383 | }); 384 | 385 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 386 | { 387 | b.HasOne("DataAccess.Data.User", null) 388 | .WithMany() 389 | .HasForeignKey("UserId") 390 | .OnDelete(DeleteBehavior.Cascade) 391 | .IsRequired(); 392 | }); 393 | 394 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 395 | { 396 | b.HasOne("DataAccess.Data.User", null) 397 | .WithMany() 398 | .HasForeignKey("UserId") 399 | .OnDelete(DeleteBehavior.Cascade) 400 | .IsRequired(); 401 | }); 402 | 403 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 404 | { 405 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) 406 | .WithMany() 407 | .HasForeignKey("RoleId") 408 | .OnDelete(DeleteBehavior.Cascade) 409 | .IsRequired(); 410 | 411 | b.HasOne("DataAccess.Data.User", null) 412 | .WithMany() 413 | .HasForeignKey("UserId") 414 | .OnDelete(DeleteBehavior.Cascade) 415 | .IsRequired(); 416 | }); 417 | 418 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 419 | { 420 | b.HasOne("DataAccess.Data.User", null) 421 | .WithMany() 422 | .HasForeignKey("UserId") 423 | .OnDelete(DeleteBehavior.Cascade) 424 | .IsRequired(); 425 | }); 426 | 427 | modelBuilder.Entity("DataAccess.Data.Category", b => 428 | { 429 | b.Navigation("Rooms"); 430 | }); 431 | 432 | modelBuilder.Entity("DataAccess.Data.Room", b => 433 | { 434 | b.Navigation("Reservations"); 435 | }); 436 | 437 | modelBuilder.Entity("DataAccess.Data.User", b => 438 | { 439 | b.Navigation("RefreshTokens"); 440 | 441 | b.Navigation("Reservations"); 442 | }); 443 | #pragma warning restore 612, 618 444 | } 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /DataAccess/Migrations/20240308204547_Initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using DataAccess.Data; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 9 | 10 | #nullable disable 11 | 12 | namespace DataAccess.Migrations 13 | { 14 | [DbContext(typeof(ShopDbContext))] 15 | [Migration("20240308204547_Initial")] 16 | partial class Initial 17 | { 18 | /// 19 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 20 | { 21 | #pragma warning disable 612, 618 22 | modelBuilder 23 | .HasAnnotation("ProductVersion", "7.0.16") 24 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 25 | 26 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); 27 | 28 | modelBuilder.Entity("BusinessLogic.Entities.RefreshToken", b => 29 | { 30 | b.Property("Id") 31 | .ValueGeneratedOnAdd() 32 | .HasColumnType("int"); 33 | 34 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 35 | 36 | b.Property("CreationDate") 37 | .HasColumnType("datetime2"); 38 | 39 | b.Property("Token") 40 | .IsRequired() 41 | .HasColumnType("nvarchar(max)"); 42 | 43 | b.Property("UserId") 44 | .IsRequired() 45 | .HasColumnType("nvarchar(450)"); 46 | 47 | b.HasKey("Id"); 48 | 49 | b.HasIndex("UserId"); 50 | 51 | b.ToTable("RefreshTokens", (string)null); 52 | }); 53 | 54 | modelBuilder.Entity("DataAccess.Data.Category", b => 55 | { 56 | b.Property("Id") 57 | .ValueGeneratedOnAdd() 58 | .HasColumnType("int"); 59 | 60 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 61 | 62 | b.Property("Name") 63 | .IsRequired() 64 | .HasColumnType("nvarchar(max)"); 65 | 66 | b.HasKey("Id"); 67 | 68 | b.ToTable("Category"); 69 | }); 70 | 71 | modelBuilder.Entity("DataAccess.Data.Reservation", b => 72 | { 73 | b.Property("Id") 74 | .ValueGeneratedOnAdd() 75 | .HasColumnType("int"); 76 | 77 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 78 | 79 | b.Property("CheckInDate") 80 | .HasColumnType("datetime2"); 81 | 82 | b.Property("CheckOutDate") 83 | .HasColumnType("datetime2"); 84 | 85 | b.Property("RoomId") 86 | .HasColumnType("int"); 87 | 88 | b.Property("UserId") 89 | .IsRequired() 90 | .HasColumnType("nvarchar(450)"); 91 | 92 | b.HasKey("Id"); 93 | 94 | b.HasIndex("RoomId"); 95 | 96 | b.HasIndex("UserId"); 97 | 98 | b.ToTable("Reservation"); 99 | }); 100 | 101 | modelBuilder.Entity("DataAccess.Data.Room", b => 102 | { 103 | b.Property("Id") 104 | .ValueGeneratedOnAdd() 105 | .HasColumnType("int"); 106 | 107 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 108 | 109 | b.Property("CategoryId") 110 | .HasColumnType("int"); 111 | 112 | b.Property("ImageUrl") 113 | .IsRequired() 114 | .HasColumnType("nvarchar(max)"); 115 | 116 | b.Property("Price") 117 | .HasColumnType("decimal(18,2)"); 118 | 119 | b.HasKey("Id"); 120 | 121 | b.HasIndex("CategoryId"); 122 | 123 | b.ToTable("Room"); 124 | }); 125 | 126 | modelBuilder.Entity("DataAccess.Data.User", b => 127 | { 128 | b.Property("Id") 129 | .HasColumnType("nvarchar(450)"); 130 | 131 | b.Property("AccessFailedCount") 132 | .HasColumnType("int"); 133 | 134 | b.Property("Birthdate") 135 | .HasColumnType("datetime2"); 136 | 137 | b.Property("ClientType") 138 | .HasColumnType("int"); 139 | 140 | b.Property("ConcurrencyStamp") 141 | .IsConcurrencyToken() 142 | .HasColumnType("nvarchar(max)"); 143 | 144 | b.Property("Email") 145 | .HasMaxLength(256) 146 | .HasColumnType("nvarchar(256)"); 147 | 148 | b.Property("EmailConfirmed") 149 | .HasColumnType("bit"); 150 | 151 | b.Property("LockoutEnabled") 152 | .HasColumnType("bit"); 153 | 154 | b.Property("LockoutEnd") 155 | .HasColumnType("datetimeoffset"); 156 | 157 | b.Property("Name") 158 | .IsRequired() 159 | .HasColumnType("nvarchar(max)"); 160 | 161 | b.Property("NormalizedEmail") 162 | .HasMaxLength(256) 163 | .HasColumnType("nvarchar(256)"); 164 | 165 | b.Property("NormalizedUserName") 166 | .HasMaxLength(256) 167 | .HasColumnType("nvarchar(256)"); 168 | 169 | b.Property("PasswordHash") 170 | .HasColumnType("nvarchar(max)"); 171 | 172 | b.Property("PhoneNumber") 173 | .HasColumnType("nvarchar(max)"); 174 | 175 | b.Property("PhoneNumberConfirmed") 176 | .HasColumnType("bit"); 177 | 178 | b.Property("SecurityStamp") 179 | .HasColumnType("nvarchar(max)"); 180 | 181 | b.Property("Surname") 182 | .IsRequired() 183 | .HasColumnType("nvarchar(max)"); 184 | 185 | b.Property("TwoFactorEnabled") 186 | .HasColumnType("bit"); 187 | 188 | b.Property("UserName") 189 | .HasMaxLength(256) 190 | .HasColumnType("nvarchar(256)"); 191 | 192 | b.HasKey("Id"); 193 | 194 | b.HasIndex("NormalizedEmail") 195 | .HasDatabaseName("EmailIndex"); 196 | 197 | b.HasIndex("NormalizedUserName") 198 | .IsUnique() 199 | .HasDatabaseName("UserNameIndex") 200 | .HasFilter("[NormalizedUserName] IS NOT NULL"); 201 | 202 | b.ToTable("AspNetUsers", (string)null); 203 | }); 204 | 205 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => 206 | { 207 | b.Property("Id") 208 | .HasColumnType("nvarchar(450)"); 209 | 210 | b.Property("ConcurrencyStamp") 211 | .IsConcurrencyToken() 212 | .HasColumnType("nvarchar(max)"); 213 | 214 | b.Property("Name") 215 | .HasMaxLength(256) 216 | .HasColumnType("nvarchar(256)"); 217 | 218 | b.Property("NormalizedName") 219 | .HasMaxLength(256) 220 | .HasColumnType("nvarchar(256)"); 221 | 222 | b.HasKey("Id"); 223 | 224 | b.HasIndex("NormalizedName") 225 | .IsUnique() 226 | .HasDatabaseName("RoleNameIndex") 227 | .HasFilter("[NormalizedName] IS NOT NULL"); 228 | 229 | b.ToTable("AspNetRoles", (string)null); 230 | }); 231 | 232 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 233 | { 234 | b.Property("Id") 235 | .ValueGeneratedOnAdd() 236 | .HasColumnType("int"); 237 | 238 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 239 | 240 | b.Property("ClaimType") 241 | .HasColumnType("nvarchar(max)"); 242 | 243 | b.Property("ClaimValue") 244 | .HasColumnType("nvarchar(max)"); 245 | 246 | b.Property("RoleId") 247 | .IsRequired() 248 | .HasColumnType("nvarchar(450)"); 249 | 250 | b.HasKey("Id"); 251 | 252 | b.HasIndex("RoleId"); 253 | 254 | b.ToTable("AspNetRoleClaims", (string)null); 255 | }); 256 | 257 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 258 | { 259 | b.Property("Id") 260 | .ValueGeneratedOnAdd() 261 | .HasColumnType("int"); 262 | 263 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); 264 | 265 | b.Property("ClaimType") 266 | .HasColumnType("nvarchar(max)"); 267 | 268 | b.Property("ClaimValue") 269 | .HasColumnType("nvarchar(max)"); 270 | 271 | b.Property("UserId") 272 | .IsRequired() 273 | .HasColumnType("nvarchar(450)"); 274 | 275 | b.HasKey("Id"); 276 | 277 | b.HasIndex("UserId"); 278 | 279 | b.ToTable("AspNetUserClaims", (string)null); 280 | }); 281 | 282 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 283 | { 284 | b.Property("LoginProvider") 285 | .HasColumnType("nvarchar(450)"); 286 | 287 | b.Property("ProviderKey") 288 | .HasColumnType("nvarchar(450)"); 289 | 290 | b.Property("ProviderDisplayName") 291 | .HasColumnType("nvarchar(max)"); 292 | 293 | b.Property("UserId") 294 | .IsRequired() 295 | .HasColumnType("nvarchar(450)"); 296 | 297 | b.HasKey("LoginProvider", "ProviderKey"); 298 | 299 | b.HasIndex("UserId"); 300 | 301 | b.ToTable("AspNetUserLogins", (string)null); 302 | }); 303 | 304 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 305 | { 306 | b.Property("UserId") 307 | .HasColumnType("nvarchar(450)"); 308 | 309 | b.Property("RoleId") 310 | .HasColumnType("nvarchar(450)"); 311 | 312 | b.HasKey("UserId", "RoleId"); 313 | 314 | b.HasIndex("RoleId"); 315 | 316 | b.ToTable("AspNetUserRoles", (string)null); 317 | }); 318 | 319 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 320 | { 321 | b.Property("UserId") 322 | .HasColumnType("nvarchar(450)"); 323 | 324 | b.Property("LoginProvider") 325 | .HasColumnType("nvarchar(450)"); 326 | 327 | b.Property("Name") 328 | .HasColumnType("nvarchar(450)"); 329 | 330 | b.Property("Value") 331 | .HasColumnType("nvarchar(max)"); 332 | 333 | b.HasKey("UserId", "LoginProvider", "Name"); 334 | 335 | b.ToTable("AspNetUserTokens", (string)null); 336 | }); 337 | 338 | modelBuilder.Entity("BusinessLogic.Entities.RefreshToken", b => 339 | { 340 | b.HasOne("DataAccess.Data.User", "User") 341 | .WithMany("RefreshTokens") 342 | .HasForeignKey("UserId") 343 | .OnDelete(DeleteBehavior.Cascade) 344 | .IsRequired(); 345 | 346 | b.Navigation("User"); 347 | }); 348 | 349 | modelBuilder.Entity("DataAccess.Data.Reservation", b => 350 | { 351 | b.HasOne("DataAccess.Data.Room", "Room") 352 | .WithMany("Reservations") 353 | .HasForeignKey("RoomId") 354 | .OnDelete(DeleteBehavior.Cascade) 355 | .IsRequired(); 356 | 357 | b.HasOne("DataAccess.Data.User", "User") 358 | .WithMany("Reservations") 359 | .HasForeignKey("UserId") 360 | .OnDelete(DeleteBehavior.Cascade) 361 | .IsRequired(); 362 | 363 | b.Navigation("Room"); 364 | 365 | b.Navigation("User"); 366 | }); 367 | 368 | modelBuilder.Entity("DataAccess.Data.Room", b => 369 | { 370 | b.HasOne("DataAccess.Data.Category", "Category") 371 | .WithMany("Rooms") 372 | .HasForeignKey("CategoryId") 373 | .OnDelete(DeleteBehavior.Cascade) 374 | .IsRequired(); 375 | 376 | b.Navigation("Category"); 377 | }); 378 | 379 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 380 | { 381 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) 382 | .WithMany() 383 | .HasForeignKey("RoleId") 384 | .OnDelete(DeleteBehavior.Cascade) 385 | .IsRequired(); 386 | }); 387 | 388 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 389 | { 390 | b.HasOne("DataAccess.Data.User", null) 391 | .WithMany() 392 | .HasForeignKey("UserId") 393 | .OnDelete(DeleteBehavior.Cascade) 394 | .IsRequired(); 395 | }); 396 | 397 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 398 | { 399 | b.HasOne("DataAccess.Data.User", null) 400 | .WithMany() 401 | .HasForeignKey("UserId") 402 | .OnDelete(DeleteBehavior.Cascade) 403 | .IsRequired(); 404 | }); 405 | 406 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 407 | { 408 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) 409 | .WithMany() 410 | .HasForeignKey("RoleId") 411 | .OnDelete(DeleteBehavior.Cascade) 412 | .IsRequired(); 413 | 414 | b.HasOne("DataAccess.Data.User", null) 415 | .WithMany() 416 | .HasForeignKey("UserId") 417 | .OnDelete(DeleteBehavior.Cascade) 418 | .IsRequired(); 419 | }); 420 | 421 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 422 | { 423 | b.HasOne("DataAccess.Data.User", null) 424 | .WithMany() 425 | .HasForeignKey("UserId") 426 | .OnDelete(DeleteBehavior.Cascade) 427 | .IsRequired(); 428 | }); 429 | 430 | modelBuilder.Entity("DataAccess.Data.Category", b => 431 | { 432 | b.Navigation("Rooms"); 433 | }); 434 | 435 | modelBuilder.Entity("DataAccess.Data.Room", b => 436 | { 437 | b.Navigation("Reservations"); 438 | }); 439 | 440 | modelBuilder.Entity("DataAccess.Data.User", b => 441 | { 442 | b.Navigation("RefreshTokens"); 443 | 444 | b.Navigation("Reservations"); 445 | }); 446 | #pragma warning restore 612, 618 447 | } 448 | } 449 | } 450 | --------------------------------------------------------------------------------