Add JWT authentication and role-based authorization
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Hospitality.Backend.Configuration;
|
||||||
|
|
||||||
|
public class JwtSettings
|
||||||
|
{
|
||||||
|
public string SecretKey { get; set; } = string.Empty;
|
||||||
|
public string Issuer { get; set; } = string.Empty;
|
||||||
|
public string Audience { get; set; } = string.Empty;
|
||||||
|
public int ExpirationMinutes { get; set; } = 60;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Hospitality.Backend.DTOs;
|
||||||
|
using Hospitality.Backend.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Hospitality.Backend.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class AuthController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IAuthService _authService;
|
||||||
|
|
||||||
|
public AuthController(IAuthService authService)
|
||||||
|
{
|
||||||
|
_authService = authService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("login")]
|
||||||
|
public async Task<ActionResult<LoginResponse>> Login([FromBody] LoginRequest request)
|
||||||
|
{
|
||||||
|
var response = await _authService.LoginAsync(request);
|
||||||
|
if (response == null)
|
||||||
|
return Unauthorized(new { message = "Invalid email or password" });
|
||||||
|
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("register")]
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
public async Task<ActionResult> Register([FromBody] RegisterRequest request)
|
||||||
|
{
|
||||||
|
var success = await _authService.RegisterAsync(request);
|
||||||
|
if (!success)
|
||||||
|
return BadRequest(new { message = "Registration failed" });
|
||||||
|
|
||||||
|
return Ok(new { message = "User registered successfully" });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("me")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<UserInfoResponse>> GetCurrentUser()
|
||||||
|
{
|
||||||
|
var email = User.FindFirstValue(ClaimTypes.Email);
|
||||||
|
if (email == null)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var userInfo = await _authService.GetUserInfoAsync(email);
|
||||||
|
if (userInfo == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(userInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Hospitality.Backend.DTOs;
|
||||||
|
|
||||||
|
public record LoginRequest(
|
||||||
|
string Email,
|
||||||
|
string Password
|
||||||
|
);
|
||||||
|
|
||||||
|
public record LoginResponse(
|
||||||
|
string Token,
|
||||||
|
string Email,
|
||||||
|
string[] Roles
|
||||||
|
);
|
||||||
|
|
||||||
|
public record RegisterRequest(
|
||||||
|
string Email,
|
||||||
|
string Password,
|
||||||
|
string Role
|
||||||
|
);
|
||||||
|
|
||||||
|
public record UserInfoResponse(
|
||||||
|
string Email,
|
||||||
|
string[] Roles
|
||||||
|
);
|
||||||
@@ -1,19 +1,34 @@
|
|||||||
using Hospitality.Domain.Entities;
|
using Hospitality.Domain.Entities;
|
||||||
using Hospitality.Domain.Enums;
|
using Hospitality.Domain.Enums;
|
||||||
using Hospitality.Infrastructure.Data;
|
using Hospitality.Infrastructure.Data;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Hospitality.Backend.Data;
|
namespace Hospitality.Backend.Data;
|
||||||
|
|
||||||
public static class DbSeeder
|
public static class DbSeeder
|
||||||
{
|
{
|
||||||
public static async Task SeedAsync(HospitalityDbContext context)
|
public static async Task SeedAsync(HospitalityDbContext context, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
|
||||||
{
|
{
|
||||||
// Check if already seeded
|
// Check if data already exists
|
||||||
if (context.Events.Any())
|
if (await context.Events.AnyAsync())
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Seed Roles
|
||||||
|
var roles = new[] { "Admin", "Staff", "Guest" };
|
||||||
|
foreach (var roleName in roles)
|
||||||
|
{
|
||||||
|
if (!await roleManager.RoleExistsAsync(roleName))
|
||||||
|
{
|
||||||
|
await roleManager.CreateAsync(new IdentityRole(roleName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seed Users
|
||||||
|
await SeedUserAsync(userManager, "admin@example.com", "Admin123!", "Admin");
|
||||||
|
await SeedUserAsync(userManager, "staff@example.com", "Staff123!", "Staff");
|
||||||
|
var guestUser = await SeedUserAsync(userManager, "guest@example.com", "Guest123!", "Guest");
|
||||||
|
|
||||||
// Create Events
|
// Create Events
|
||||||
var summerFestival = new Event
|
var summerFestival = new Event
|
||||||
{
|
{
|
||||||
@@ -359,6 +374,36 @@ public static class DbSeeder
|
|||||||
|
|
||||||
context.Transactions.AddRange(transactions);
|
context.Transactions.AddRange(transactions);
|
||||||
|
|
||||||
|
// Link guest user to John Doe (for testing guest view)
|
||||||
|
if (guestUser != null)
|
||||||
|
{
|
||||||
|
guestUser.PersonId = johnDoe.Id;
|
||||||
|
await userManager.UpdateAsync(guestUser);
|
||||||
|
}
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<ApplicationUser?> SeedUserAsync(UserManager<ApplicationUser> userManager, string email, string password, string role)
|
||||||
|
{
|
||||||
|
var existingUser = await userManager.FindByEmailAsync(email);
|
||||||
|
if (existingUser != null)
|
||||||
|
return existingUser;
|
||||||
|
|
||||||
|
var user = new ApplicationUser
|
||||||
|
{
|
||||||
|
UserName = email,
|
||||||
|
Email = email,
|
||||||
|
EmailConfirmed = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await userManager.CreateAsync(user, password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
await userManager.AddToRoleAsync(user, role);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.0.1" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.0.1" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Hospitality.Backend.Configuration;
|
||||||
using Hospitality.Backend.Services;
|
using Hospitality.Backend.Services;
|
||||||
|
using Hospitality.Domain.Entities;
|
||||||
using Hospitality.Infrastructure.Data;
|
using Hospitality.Infrastructure.Data;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -12,7 +18,44 @@ builder.Services.AddSwaggerGen();
|
|||||||
builder.Services.AddDbContext<HospitalityDbContext>(options =>
|
builder.Services.AddDbContext<HospitalityDbContext>(options =>
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||||
|
|
||||||
|
// Configure JWT settings
|
||||||
|
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("JwtSettings"));
|
||||||
|
var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>();
|
||||||
|
|
||||||
|
// Add Identity
|
||||||
|
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
||||||
|
{
|
||||||
|
options.Password.RequireDigit = true;
|
||||||
|
options.Password.RequireLowercase = true;
|
||||||
|
options.Password.RequireUppercase = true;
|
||||||
|
options.Password.RequireNonAlphanumeric = true;
|
||||||
|
options.Password.RequiredLength = 8;
|
||||||
|
})
|
||||||
|
.AddEntityFrameworkStores<HospitalityDbContext>()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
// Add JWT Authentication
|
||||||
|
builder.Services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
})
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ValidIssuer = jwtSettings!.Issuer,
|
||||||
|
ValidAudience = jwtSettings.Audience,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// Register services
|
// Register services
|
||||||
|
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||||
builder.Services.AddScoped<IEventService, EventService>();
|
builder.Services.AddScoped<IEventService, EventService>();
|
||||||
builder.Services.AddScoped<IGroupService, GroupService>();
|
builder.Services.AddScoped<IGroupService, GroupService>();
|
||||||
builder.Services.AddScoped<IProductService, ProductService>();
|
builder.Services.AddScoped<IProductService, ProductService>();
|
||||||
@@ -37,7 +80,13 @@ var app = builder.Build();
|
|||||||
using (var scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var context = scope.ServiceProvider.GetRequiredService<HospitalityDbContext>();
|
var context = scope.ServiceProvider.GetRequiredService<HospitalityDbContext>();
|
||||||
await Hospitality.Backend.Data.DbSeeder.SeedAsync(context);
|
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||||
|
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||||
|
|
||||||
|
// Apply migrations
|
||||||
|
await context.Database.MigrateAsync();
|
||||||
|
|
||||||
|
await Hospitality.Backend.Data.DbSeeder.SeedAsync(context, userManager, roleManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -52,6 +101,7 @@ app.UseHttpsRedirection();
|
|||||||
|
|
||||||
app.UseCors("AllowFrontend");
|
app.UseCors("AllowFrontend");
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using Hospitality.Backend.Configuration;
|
||||||
|
using Hospitality.Backend.DTOs;
|
||||||
|
using Hospitality.Domain.Entities;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace Hospitality.Backend.Services;
|
||||||
|
|
||||||
|
public class AuthService : IAuthService
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
private readonly JwtSettings _jwtSettings;
|
||||||
|
|
||||||
|
public AuthService(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
IOptions<JwtSettings> jwtSettings)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_jwtSettings = jwtSettings.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LoginResponse?> LoginAsync(LoginRequest request)
|
||||||
|
{
|
||||||
|
var user = await _userManager.FindByEmailAsync(request.Email);
|
||||||
|
if (user == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, false);
|
||||||
|
if (!result.Succeeded)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var roles = await _userManager.GetRolesAsync(user);
|
||||||
|
var token = GenerateJwtToken(user, roles.ToArray());
|
||||||
|
|
||||||
|
return new LoginResponse(token, user.Email!, roles.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> RegisterAsync(RegisterRequest request)
|
||||||
|
{
|
||||||
|
var user = new ApplicationUser
|
||||||
|
{
|
||||||
|
UserName = request.Email,
|
||||||
|
Email = request.Email
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _userManager.CreateAsync(user, request.Password);
|
||||||
|
if (!result.Succeeded)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
await _userManager.AddToRoleAsync(user, request.Role);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserInfoResponse?> GetUserInfoAsync(string email)
|
||||||
|
{
|
||||||
|
var user = await _userManager.FindByEmailAsync(email);
|
||||||
|
if (user == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var roles = await _userManager.GetRolesAsync(user);
|
||||||
|
return new UserInfoResponse(user.Email!, roles.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GenerateJwtToken(ApplicationUser user, string[] roles)
|
||||||
|
{
|
||||||
|
var claims = new List<Claim>
|
||||||
|
{
|
||||||
|
new(ClaimTypes.NameIdentifier, user.Id),
|
||||||
|
new(ClaimTypes.Email, user.Email!),
|
||||||
|
new(JwtRegisteredClaimNames.Sub, user.Email!),
|
||||||
|
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||||
|
};
|
||||||
|
|
||||||
|
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
|
||||||
|
|
||||||
|
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
|
||||||
|
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||||
|
|
||||||
|
var token = new JwtSecurityToken(
|
||||||
|
issuer: _jwtSettings.Issuer,
|
||||||
|
audience: _jwtSettings.Audience,
|
||||||
|
claims: claims,
|
||||||
|
expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpirationMinutes),
|
||||||
|
signingCredentials: creds
|
||||||
|
);
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Hospitality.Backend.DTOs;
|
||||||
|
|
||||||
|
namespace Hospitality.Backend.Services;
|
||||||
|
|
||||||
|
public interface IAuthService
|
||||||
|
{
|
||||||
|
Task<LoginResponse?> LoginAsync(LoginRequest request);
|
||||||
|
Task<bool> RegisterAsync(RegisterRequest request);
|
||||||
|
Task<UserInfoResponse?> GetUserInfoAsync(string email);
|
||||||
|
}
|
||||||
@@ -2,6 +2,12 @@
|
|||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Host=localhost;Database=hospitality;Username=postgres;Password=password"
|
"DefaultConnection": "Host=localhost;Database=hospitality;Username=postgres;Password=password"
|
||||||
},
|
},
|
||||||
|
"JwtSettings": {
|
||||||
|
"SecretKey": "ThisIsAVerySecureSecretKeyForDevelopmentOnly123!",
|
||||||
|
"Issuer": "HospitalityAPI",
|
||||||
|
"Audience": "HospitalityWeb",
|
||||||
|
"ExpirationMinutes": 480
|
||||||
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Hospitality.Domain.Entities;
|
||||||
|
|
||||||
|
public class ApplicationUser : IdentityUser
|
||||||
|
{
|
||||||
|
// Optional: Link to Person entity for guest users
|
||||||
|
public Guid? PersonId { get; set; }
|
||||||
|
public Person? Person { get; set; }
|
||||||
|
}
|
||||||
@@ -6,4 +6,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using Hospitality.Domain.Entities;
|
using Hospitality.Domain.Entities;
|
||||||
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Hospitality.Infrastructure.Data;
|
namespace Hospitality.Infrastructure.Data;
|
||||||
|
|
||||||
public class HospitalityDbContext : DbContext
|
public class HospitalityDbContext : IdentityDbContext<ApplicationUser>
|
||||||
{
|
{
|
||||||
public HospitalityDbContext(DbContextOptions<HospitalityDbContext> options) : base(options)
|
public HospitalityDbContext(DbContextOptions<HospitalityDbContext> options) : base(options)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
|||||||
+587
@@ -0,0 +1,587 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Hospitality.Infrastructure.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hospitality.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(HospitalityDbContext))]
|
||||||
|
[Migration("20251123211539_AddIdentity")]
|
||||||
|
partial class AddIdentity
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "9.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid?>("PersonId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.HasIndex("PersonId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Event", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("EndDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("StartDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Events");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Group", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("ContactEmail")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ContactPersonName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("EventId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.ToTable("Groups");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Person", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("GroupId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("QrCode")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GroupId");
|
||||||
|
|
||||||
|
b.HasIndex("QrCode")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("People");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.PersonQuota", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("InitialAmount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<Guid>("PersonId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProductId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("UsedAmount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PersonId");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("PersonQuotas");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Product", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("EventId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.ToTable("Products");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.QuotaDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("GroupId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("InitialAmount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProductId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GroupId");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("QuotaDefinitions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("Amount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<Guid>("PersonId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProductId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid?>("StaffId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Timestamp")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PersonId");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Person", "Person")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PersonId");
|
||||||
|
|
||||||
|
b.Navigation("Person");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Group", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Event", "Event")
|
||||||
|
.WithMany("Groups")
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Person", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Group", "Group")
|
||||||
|
.WithMany("People")
|
||||||
|
.HasForeignKey("GroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Group");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.PersonQuota", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Person", "Person")
|
||||||
|
.WithMany("Quotas")
|
||||||
|
.HasForeignKey("PersonId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Product", "Product")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProductId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Person");
|
||||||
|
|
||||||
|
b.Navigation("Product");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Product", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Event", "Event")
|
||||||
|
.WithMany("Products")
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.QuotaDefinition", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Group", "Group")
|
||||||
|
.WithMany("DefaultQuotas")
|
||||||
|
.HasForeignKey("GroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Product", "Product")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProductId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Group");
|
||||||
|
|
||||||
|
b.Navigation("Product");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Person", "Person")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PersonId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Product", "Product")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProductId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Person");
|
||||||
|
|
||||||
|
b.Navigation("Product");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Event", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Groups");
|
||||||
|
|
||||||
|
b.Navigation("Products");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Group", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("DefaultQuotas");
|
||||||
|
|
||||||
|
b.Navigation("People");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.Person", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Quotas");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hospitality.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddIdentity : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUsers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "text", nullable: false),
|
||||||
|
PersonId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
PasswordHash = table.Column<string>(type: "text", nullable: true),
|
||||||
|
SecurityStamp = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(type: "text", nullable: true),
|
||||||
|
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUsers_People_PersonId",
|
||||||
|
column: x => x.PersonId,
|
||||||
|
principalTable: "People",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoleClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
RoleId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserLogins",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ProviderKey = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
RoleId = table.Column<string>(type: "text", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserTokens",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Value = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetRoleClaims_RoleId",
|
||||||
|
table: "AspNetRoleClaims",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles",
|
||||||
|
column: "NormalizedName",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserClaims_UserId",
|
||||||
|
table: "AspNetUserClaims",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserLogins_UserId",
|
||||||
|
table: "AspNetUserLogins",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserRoles_RoleId",
|
||||||
|
table: "AspNetUserRoles",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "EmailIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedEmail");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUsers_PersonId",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "PersonId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedUserName",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoleClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserLogins");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserTokens");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,75 @@ namespace Hospitality.Infrastructure.Migrations
|
|||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid?>("PersonId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.HasIndex("PersonId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hospitality.Domain.Entities.Event", b =>
|
modelBuilder.Entity("Hospitality.Domain.Entities.Event", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -209,6 +278,147 @@ namespace Hospitality.Infrastructure.Migrations
|
|||||||
b.ToTable("Transactions");
|
b.ToTable("Transactions");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hospitality.Domain.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.Person", "Person")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PersonId");
|
||||||
|
|
||||||
|
b.Navigation("Person");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hospitality.Domain.Entities.Group", b =>
|
modelBuilder.Entity("Hospitality.Domain.Entities.Group", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Hospitality.Domain.Entities.Event", "Event")
|
b.HasOne("Hospitality.Domain.Entities.Event", "Event")
|
||||||
@@ -299,6 +509,57 @@ namespace Hospitality.Infrastructure.Migrations
|
|||||||
b.Navigation("Product");
|
b.Navigation("Product");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hospitality.Domain.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hospitality.Domain.Entities.Event", b =>
|
modelBuilder.Entity("Hospitality.Domain.Entities.Event", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Groups");
|
b.Navigation("Groups");
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
|
import { BrowserRouter, Routes, Route, Link, useNavigate } from 'react-router-dom';
|
||||||
import { Navbar, Nav, Container } from 'react-bootstrap';
|
import { Navbar, Nav, Container, Button } from 'react-bootstrap';
|
||||||
|
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
||||||
|
import { ProtectedRoute } from './components/ProtectedRoute';
|
||||||
|
import { LoginPage } from './pages/LoginPage';
|
||||||
import EventsPage from './pages/admin/EventsPage';
|
import EventsPage from './pages/admin/EventsPage';
|
||||||
import EventDetailPage from './pages/admin/EventDetailPage';
|
import EventDetailPage from './pages/admin/EventDetailPage';
|
||||||
import GroupDetailPage from './pages/admin/GroupDetailPage';
|
import GroupDetailPage from './pages/admin/GroupDetailPage';
|
||||||
@@ -9,33 +12,96 @@ import GuestQrPage from './pages/guest/GuestQrPage';
|
|||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
function NavbarContent() {
|
||||||
|
const { isAuthenticated, user, logout, hasRole } = useAuth();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
logout();
|
||||||
|
navigate('/login');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Navbar bg="primary" variant="dark" expand="lg" className="mb-4">
|
||||||
|
<Container>
|
||||||
|
<Navbar.Brand as={Link} to="/">Hospitality System</Navbar.Brand>
|
||||||
|
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
||||||
|
<Navbar.Collapse id="basic-navbar-nav">
|
||||||
|
{isAuthenticated && (
|
||||||
|
<>
|
||||||
|
<Nav className="me-auto">
|
||||||
|
{hasRole('Admin') && <Nav.Link as={Link} to="/">Admin</Nav.Link>}
|
||||||
|
{hasRole('Staff') && <Nav.Link as={Link} to="/staff">Staff Scanner</Nav.Link>}
|
||||||
|
{hasRole('Guest') && <Nav.Link as={Link} to="/guest">Guest View</Nav.Link>}
|
||||||
|
</Nav>
|
||||||
|
<Nav>
|
||||||
|
<Navbar.Text className="me-3">
|
||||||
|
{user?.email} ({user?.roles.join(', ')})
|
||||||
|
</Navbar.Text>
|
||||||
|
<Button variant="outline-light" size="sm" onClick={handleLogout}>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
</Nav>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Navbar.Collapse>
|
||||||
|
</Container>
|
||||||
|
</Navbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Navbar bg="primary" variant="dark" expand="lg" className="mb-4">
|
<AuthProvider>
|
||||||
|
<NavbarContent />
|
||||||
<Container>
|
<Container>
|
||||||
<Navbar.Brand as={Link} to="/">Hospitality System</Navbar.Brand>
|
<Routes>
|
||||||
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Navbar.Collapse id="basic-navbar-nav">
|
<Route
|
||||||
<Nav className="ms-auto">
|
path="/"
|
||||||
<Nav.Link as={Link} to="/">Admin</Nav.Link>
|
element={
|
||||||
<Nav.Link as={Link} to="/staff">Staff Scanner</Nav.Link>
|
<ProtectedRoute roles={['Admin']}>
|
||||||
<Nav.Link as={Link} to="/guest">Guest View</Nav.Link>
|
<EventsPage />
|
||||||
</Nav>
|
</ProtectedRoute>
|
||||||
</Navbar.Collapse>
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/events/:id"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute roles={['Admin']}>
|
||||||
|
<EventDetailPage />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/groups/:id"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute roles={['Admin']}>
|
||||||
|
<GroupDetailPage />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/staff"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute roles={['Staff']}>
|
||||||
|
<ScannerPage />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/guest"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute roles={['Guest']}>
|
||||||
|
<GuestQrPage />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
</Container>
|
</Container>
|
||||||
</Navbar>
|
</AuthProvider>
|
||||||
|
|
||||||
<Container>
|
|
||||||
<Routes>
|
|
||||||
<Route path="/" element={<EventsPage />} />
|
|
||||||
<Route path="/events/:id" element={<EventDetailPage />} />
|
|
||||||
<Route path="/groups/:id" element={<GroupDetailPage />} />
|
|
||||||
<Route path="/staff" element={<ScannerPage />} />
|
|
||||||
<Route path="/guest" element={<GuestQrPage />} />
|
|
||||||
</Routes>
|
|
||||||
</Container>
|
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Navigate } from 'react-router-dom';
|
||||||
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
|
|
||||||
|
interface ProtectedRouteProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
roles?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children, roles }) => {
|
||||||
|
const { isAuthenticated, hasRole } = useAuth();
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
return <Navigate to="/login" replace />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roles && roles.length > 0) {
|
||||||
|
const hasRequiredRole = roles.some((role) => hasRole(role));
|
||||||
|
if (!hasRequiredRole) {
|
||||||
|
return <Navigate to="/login" replace />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||||
|
import { authApi, LoginRequest, UserInfo } from '../lib/api';
|
||||||
|
|
||||||
|
interface AuthContextType {
|
||||||
|
user: UserInfo | null;
|
||||||
|
token: string | null;
|
||||||
|
login: (data: LoginRequest) => Promise<void>;
|
||||||
|
logout: () => void;
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
hasRole: (role: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const [user, setUser] = useState<UserInfo | null>(null);
|
||||||
|
const [token, setToken] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Load from localStorage on mount
|
||||||
|
const storedToken = localStorage.getItem('token');
|
||||||
|
const storedUser = localStorage.getItem('user');
|
||||||
|
|
||||||
|
if (storedToken && storedUser) {
|
||||||
|
setToken(storedToken);
|
||||||
|
setUser(JSON.parse(storedUser));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const login = async (data: LoginRequest) => {
|
||||||
|
const response = await authApi.login(data);
|
||||||
|
const userInfo: UserInfo = {
|
||||||
|
email: response.email,
|
||||||
|
roles: response.roles,
|
||||||
|
};
|
||||||
|
|
||||||
|
setToken(response.token);
|
||||||
|
setUser(userInfo);
|
||||||
|
localStorage.setItem('token', response.token);
|
||||||
|
localStorage.setItem('user', JSON.stringify(userInfo));
|
||||||
|
};
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
setToken(null);
|
||||||
|
setUser(null);
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
localStorage.removeItem('user');
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasRole = (role: string) => {
|
||||||
|
return user?.roles.includes(role) ?? false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider
|
||||||
|
value={{
|
||||||
|
user,
|
||||||
|
token,
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
isAuthenticated: !!token,
|
||||||
|
hasRole,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAuth = () => {
|
||||||
|
const context = useContext(AuthContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useAuth must be used within AuthProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5163/api';
|
const API_BASE_URL = 'http://localhost:5163/api';
|
||||||
|
|
||||||
export const api = axios.create({
|
export const api = axios.create({
|
||||||
baseURL: API_BASE_URL,
|
baseURL: API_BASE_URL,
|
||||||
@@ -9,50 +9,56 @@ export const api = axios.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Event API
|
// Request interceptor to add JWT token
|
||||||
export const eventsApi = {
|
api.interceptors.request.use(
|
||||||
getAll: () => api.get('/events'),
|
(config) => {
|
||||||
getById: (id: string) => api.get(`/events/${id}`),
|
const token = localStorage.getItem('token');
|
||||||
create: (data: any) => api.post('/events', data),
|
if (token) {
|
||||||
update: (id: string, data: any) => api.put(`/events/${id}`, data),
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
delete: (id: string) => api.delete(`/events/${id}`),
|
}
|
||||||
};
|
return config;
|
||||||
|
},
|
||||||
|
(error) => Promise.reject(error)
|
||||||
|
);
|
||||||
|
|
||||||
// Group API
|
// Response interceptor for 401 errors
|
||||||
export const groupsApi = {
|
api.interceptors.response.use(
|
||||||
getByEventId: (eventId: string) => api.get(`/events/${eventId}/groups`),
|
(response) => response,
|
||||||
getById: (id: string) => api.get(`/groups/${id}`),
|
(error) => {
|
||||||
create: (eventId: string, data: any) => api.post(`/events/${eventId}/groups`, data),
|
if (error.response?.status === 401) {
|
||||||
update: (id: string, data: any) => api.put(`/groups/${id}`, data),
|
localStorage.removeItem('token');
|
||||||
delete: (id: string) => api.delete(`/groups/${id}`),
|
localStorage.removeItem('user');
|
||||||
};
|
window.location.href = '/login';
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// People API
|
// Auth API
|
||||||
export const peopleApi = {
|
export interface LoginRequest {
|
||||||
getById: (id: string) => api.get(`/people/${id}`),
|
email: string;
|
||||||
create: (groupId: string, data: any) => api.post(`/groups/${groupId}/people`, data),
|
password: string;
|
||||||
update: (id: string, data: any) => api.put(`/people/${id}`, data),
|
}
|
||||||
delete: (id: string) => api.delete(`/people/${id}`),
|
|
||||||
assignQuota: (personId: string, data: any) => api.post(`/people/${personId}/quotas`, data),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Product API
|
export interface LoginResponse {
|
||||||
export const productsApi = {
|
token: string;
|
||||||
getByEventId: (eventId: string) => api.get(`/events/${eventId}/products`),
|
email: string;
|
||||||
create: (eventId: string, data: any) => api.post(`/events/${eventId}/products`, data),
|
roles: string[];
|
||||||
update: (id: string, data: any) => api.put(`/products/${id}`, data),
|
}
|
||||||
delete: (id: string) => api.delete(`/products/${id}`),
|
|
||||||
};
|
|
||||||
|
|
||||||
// QR Code API
|
export interface UserInfo {
|
||||||
export const qrCodeApi = {
|
email: string;
|
||||||
getByQrCode: (qrCode: string) => api.get(`/qr/${qrCode}`),
|
roles: string[];
|
||||||
getQuotas: (qrCode: string) => api.get(`/qr/${qrCode}/quotas`),
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Transaction API
|
export const authApi = {
|
||||||
export const transactionsApi = {
|
login: async (data: LoginRequest): Promise<LoginResponse> => {
|
||||||
create: (data: any) => api.post('/transactions', data),
|
const response = await axios.post(`${API_BASE_URL}/auth/login`, data);
|
||||||
getByPersonId: (personId: string) => api.get(`/transactions/person/${personId}`),
|
return response.data;
|
||||||
getByEventId: (eventId: string) => api.get(`/transactions/event/${eventId}`),
|
},
|
||||||
|
|
||||||
|
me: async (): Promise<UserInfo> => {
|
||||||
|
const response = await api.get('/auth/me');
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { Container, Card, Form, Button, Alert } from 'react-bootstrap';
|
||||||
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
|
|
||||||
|
export const LoginPage: React.FC = () => {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { login } = useAuth();
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setError('');
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await login({ email, password });
|
||||||
|
navigate('/');
|
||||||
|
} catch (err) {
|
||||||
|
setError('Invalid email or password');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className="d-flex align-items-center justify-content-center" style={{ minHeight: '100vh' }}>
|
||||||
|
<Card style={{ maxWidth: '400px', width: '100%' }} className="shadow">
|
||||||
|
<Card.Body className="p-4">
|
||||||
|
<h2 className="text-center mb-4">Login</h2>
|
||||||
|
|
||||||
|
{error && <Alert variant="danger">{error}</Alert>}
|
||||||
|
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<Form.Group className="mb-3">
|
||||||
|
<Form.Label>Email</Form.Label>
|
||||||
|
<Form.Control
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
required
|
||||||
|
placeholder="admin@example.com"
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Form.Group className="mb-3">
|
||||||
|
<Form.Label>Password</Form.Label>
|
||||||
|
<Form.Control
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
required
|
||||||
|
placeholder="Enter password"
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
type="submit"
|
||||||
|
className="w-100"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? 'Logging in...' : 'Login'}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<div className="mt-4 text-center">
|
||||||
|
<small className="text-muted">
|
||||||
|
<strong>Test Accounts:</strong><br />
|
||||||
|
Admin: admin@example.com / Admin123!<br />
|
||||||
|
Staff: staff@example.com / Staff123!<br />
|
||||||
|
Guest: guest@example.com / Guest123!
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user