feat: авторизация для кладовщика

This commit is contained in:
2025-05-19 00:13:37 +04:00
parent de879be266
commit e8f493691f
46 changed files with 5779 additions and 46 deletions

View File

@@ -15,4 +15,6 @@ public interface IStorekeeperAdapter
StorekeeperOperationResponse RegisterStorekeeper(StorekeeperBindingModel storekeeperModel);
StorekeeperOperationResponse ChangeStorekeeperInfo(StorekeeperBindingModel storekeeperModel);
StorekeeperOperationResponse Login(StorekeeperAuthBindingModel storekeeperModel, out string token);
}

View File

@@ -8,6 +8,9 @@ public class StorekeeperOperationResponse : OperationResponse
public static StorekeeperOperationResponse OK(List<StorekeeperViewModel> data) =>
OK<StorekeeperOperationResponse, List<StorekeeperViewModel>>(data);
public static StorekeeperOperationResponse OK(string token) =>
OK<StorekeeperOperationResponse, string>(token);
public static StorekeeperOperationResponse OK(StorekeeperViewModel data) =>
OK<StorekeeperOperationResponse, StorekeeperViewModel>(data);
@@ -21,4 +24,7 @@ public class StorekeeperOperationResponse : OperationResponse
public static StorekeeperOperationResponse InternalServerError(string message) =>
InternalServerError<StorekeeperOperationResponse>(message);
public static StorekeeperOperationResponse Unauthorized(string message) =>
Unauthorized<StorekeeperOperationResponse>(message);
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.BindingModels;
public class StorekeeperAuthBindingModel
{
public required string Login { get; set; }
public required string Password { get; set; }
}

View File

@@ -46,4 +46,8 @@ public class OperationResponse
protected static TResult InternalServerError<TResult>(string? errorMessage = null)
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.InternalServerError, Result = errorMessage };
protected static TResult Unauthorized<TResult>(string? errorMessage = null)
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.Unauthorized, Result = errorMessage };
}

View File

@@ -9,6 +9,10 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup>

View File

@@ -4,9 +4,14 @@ using Microsoft.EntityFrameworkCore;
namespace BankDatabase;
internal class BankDbContext(IConfigurationDatabase configurationDatabase) : DbContext
public class BankDbContext : DbContext
{
private readonly IConfigurationDatabase? _configurationDatabase = configurationDatabase;
private readonly IConfigurationDatabase? _configurationDatabase;
public BankDbContext(IConfigurationDatabase configurationDatabase)
{
_configurationDatabase = configurationDatabase;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{

View File

@@ -0,0 +1,17 @@
using BankContracts.Infrastructure;
using Microsoft.EntityFrameworkCore.Design;
namespace BankDatabase;
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<BankDbContext>
{
public BankDbContext CreateDbContext(string[] args)
{
return new BankDbContext(new ConfigurationDatabase());
}
}
internal class ConfigurationDatabase : IConfigurationDatabase
{
public string ConnectionString => "Host=127.0.0.1;Port=5432;Database=BankTest;Username=postgres;Password=admin123;";
}

View File

@@ -0,0 +1,562 @@
// <auto-generated />
using System;
using BankDatabase;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BankDatabase.Migrations
{
[DbContext(typeof(BankDbContext))]
[Migration("20250518195627_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BankDatabase.Models.Clerk", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Login")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MiddleName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Surname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.HasIndex("Login")
.IsUnique();
b.HasIndex("PhoneNumber")
.IsUnique();
b.ToTable("Clerks");
});
modelBuilder.Entity("BankDatabase.Models.Client", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<decimal>("Balance")
.HasColumnType("numeric");
b.Property<string>("ClerkId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Surname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ClerkId");
b.ToTable("Clients");
});
modelBuilder.Entity("BankDatabase.Models.ClientCreditProgram", b =>
{
b.Property<string>("ClientId")
.HasColumnType("text");
b.Property<string>("CreditProgramId")
.HasColumnType("text");
b.HasKey("ClientId", "CreditProgramId");
b.HasIndex("CreditProgramId");
b.ToTable("CreditProgramClients");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgram", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<decimal>("Cost")
.HasColumnType("numeric");
b.Property<decimal>("MaxCost")
.HasColumnType("numeric");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PeriodId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StorekeeperId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.HasIndex("PeriodId");
b.HasIndex("StorekeeperId");
b.ToTable("CreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgramCurrency", b =>
{
b.Property<string>("CreditProgramId")
.HasColumnType("text");
b.Property<string>("CurrencyId")
.HasColumnType("text");
b.HasKey("CreditProgramId", "CurrencyId");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyCreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.Currency", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Abbreviation")
.IsRequired()
.HasColumnType("text");
b.Property<decimal>("Cost")
.HasColumnType("numeric");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StorekeeperId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Abbreviation")
.IsUnique();
b.HasIndex("StorekeeperId");
b.ToTable("Currencies");
});
modelBuilder.Entity("BankDatabase.Models.Deposit", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("ClerkId")
.IsRequired()
.HasColumnType("text");
b.Property<decimal>("Cost")
.HasColumnType("numeric");
b.Property<float>("InterestRate")
.HasColumnType("real");
b.Property<int>("Period")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ClerkId");
b.ToTable("Deposits");
});
modelBuilder.Entity("BankDatabase.Models.DepositClient", b =>
{
b.Property<string>("DepositId")
.HasColumnType("text");
b.Property<string>("ClientId")
.HasColumnType("text");
b.HasKey("DepositId", "ClientId");
b.HasIndex("ClientId");
b.ToTable("DepositClients");
});
modelBuilder.Entity("BankDatabase.Models.DepositCurrency", b =>
{
b.Property<string>("DepositId")
.HasColumnType("text");
b.Property<string>("CurrencyId")
.HasColumnType("text");
b.HasKey("DepositId", "CurrencyId");
b.HasIndex("CurrencyId");
b.ToTable("DepositCurrencies");
});
modelBuilder.Entity("BankDatabase.Models.Period", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("EndTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("StartTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("StorekeeperId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("StorekeeperId");
b.ToTable("Periods");
});
modelBuilder.Entity("BankDatabase.Models.Replenishment", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<decimal>("Amount")
.HasColumnType("numeric");
b.Property<string>("ClerkId")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<string>("DepositId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ClerkId");
b.HasIndex("DepositId");
b.ToTable("Replenishments");
});
modelBuilder.Entity("BankDatabase.Models.Storekeeper", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Login")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MiddleName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Surname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.HasIndex("Login")
.IsUnique();
b.HasIndex("PhoneNumber")
.IsUnique();
b.ToTable("Storekeepers");
});
modelBuilder.Entity("BankDatabase.Models.Client", b =>
{
b.HasOne("BankDatabase.Models.Clerk", "Clerk")
.WithMany("Clients")
.HasForeignKey("ClerkId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Clerk");
});
modelBuilder.Entity("BankDatabase.Models.ClientCreditProgram", b =>
{
b.HasOne("BankDatabase.Models.Client", "Client")
.WithMany("CreditProgramClients")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.CreditProgram", "CreditProgram")
.WithMany("CreditProgramClients")
.HasForeignKey("CreditProgramId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("CreditProgram");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgram", b =>
{
b.HasOne("BankDatabase.Models.Period", "Period")
.WithMany("CreditPrograms")
.HasForeignKey("PeriodId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Storekeeper", "Storekeeper")
.WithMany("CreditPrograms")
.HasForeignKey("StorekeeperId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Period");
b.Navigation("Storekeeper");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgramCurrency", b =>
{
b.HasOne("BankDatabase.Models.CreditProgram", "CreditProgram")
.WithMany("CurrencyCreditPrograms")
.HasForeignKey("CreditProgramId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Currency", "Currency")
.WithMany("CurrencyCreditPrograms")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreditProgram");
b.Navigation("Currency");
});
modelBuilder.Entity("BankDatabase.Models.Currency", b =>
{
b.HasOne("BankDatabase.Models.Storekeeper", "Storekeeper")
.WithMany("Currencies")
.HasForeignKey("StorekeeperId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Storekeeper");
});
modelBuilder.Entity("BankDatabase.Models.Deposit", b =>
{
b.HasOne("BankDatabase.Models.Clerk", "Clerk")
.WithMany("Deposits")
.HasForeignKey("ClerkId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Clerk");
});
modelBuilder.Entity("BankDatabase.Models.DepositClient", b =>
{
b.HasOne("BankDatabase.Models.Client", "Client")
.WithMany("DepositClients")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Deposit", "Deposit")
.WithMany("DepositClients")
.HasForeignKey("DepositId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("Deposit");
});
modelBuilder.Entity("BankDatabase.Models.DepositCurrency", b =>
{
b.HasOne("BankDatabase.Models.Currency", "Currency")
.WithMany("DepositCurrencies")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Deposit", "Deposit")
.WithMany("DepositCurrencies")
.HasForeignKey("DepositId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
b.Navigation("Deposit");
});
modelBuilder.Entity("BankDatabase.Models.Period", b =>
{
b.HasOne("BankDatabase.Models.Storekeeper", "Storekeeper")
.WithMany("Periods")
.HasForeignKey("StorekeeperId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Storekeeper");
});
modelBuilder.Entity("BankDatabase.Models.Replenishment", b =>
{
b.HasOne("BankDatabase.Models.Clerk", "Clerk")
.WithMany("Replenishments")
.HasForeignKey("ClerkId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("BankDatabase.Models.Deposit", "Deposit")
.WithMany("Replenishments")
.HasForeignKey("DepositId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Clerk");
b.Navigation("Deposit");
});
modelBuilder.Entity("BankDatabase.Models.Clerk", b =>
{
b.Navigation("Clients");
b.Navigation("Deposits");
b.Navigation("Replenishments");
});
modelBuilder.Entity("BankDatabase.Models.Client", b =>
{
b.Navigation("CreditProgramClients");
b.Navigation("DepositClients");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgram", b =>
{
b.Navigation("CreditProgramClients");
b.Navigation("CurrencyCreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.Currency", b =>
{
b.Navigation("CurrencyCreditPrograms");
b.Navigation("DepositCurrencies");
});
modelBuilder.Entity("BankDatabase.Models.Deposit", b =>
{
b.Navigation("DepositClients");
b.Navigation("DepositCurrencies");
b.Navigation("Replenishments");
});
modelBuilder.Entity("BankDatabase.Models.Period", b =>
{
b.Navigation("CreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.Storekeeper", b =>
{
b.Navigation("CreditPrograms");
b.Navigation("Currencies");
b.Navigation("Periods");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,433 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BankDatabase.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Clerks",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Surname = table.Column<string>(type: "text", nullable: false),
MiddleName = table.Column<string>(type: "text", nullable: false),
Login = table.Column<string>(type: "text", nullable: false),
Password = table.Column<string>(type: "text", nullable: false),
Email = table.Column<string>(type: "text", nullable: false),
PhoneNumber = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Clerks", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Storekeepers",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Surname = table.Column<string>(type: "text", nullable: false),
MiddleName = table.Column<string>(type: "text", nullable: false),
Login = table.Column<string>(type: "text", nullable: false),
Password = table.Column<string>(type: "text", nullable: false),
Email = table.Column<string>(type: "text", nullable: false),
PhoneNumber = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Storekeepers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Clients",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Surname = table.Column<string>(type: "text", nullable: false),
Balance = table.Column<decimal>(type: "numeric", nullable: false),
ClerkId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Clients", x => x.Id);
table.ForeignKey(
name: "FK_Clients_Clerks_ClerkId",
column: x => x.ClerkId,
principalTable: "Clerks",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Deposits",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
InterestRate = table.Column<float>(type: "real", nullable: false),
Cost = table.Column<decimal>(type: "numeric", nullable: false),
Period = table.Column<int>(type: "integer", nullable: false),
ClerkId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Deposits", x => x.Id);
table.ForeignKey(
name: "FK_Deposits_Clerks_ClerkId",
column: x => x.ClerkId,
principalTable: "Clerks",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Currencies",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Abbreviation = table.Column<string>(type: "text", nullable: false),
Cost = table.Column<decimal>(type: "numeric", nullable: false),
StorekeeperId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Currencies", x => x.Id);
table.ForeignKey(
name: "FK_Currencies_Storekeepers_StorekeeperId",
column: x => x.StorekeeperId,
principalTable: "Storekeepers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Periods",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
StartTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
EndTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
StorekeeperId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Periods", x => x.Id);
table.ForeignKey(
name: "FK_Periods_Storekeepers_StorekeeperId",
column: x => x.StorekeeperId,
principalTable: "Storekeepers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "DepositClients",
columns: table => new
{
DepositId = table.Column<string>(type: "text", nullable: false),
ClientId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DepositClients", x => new { x.DepositId, x.ClientId });
table.ForeignKey(
name: "FK_DepositClients_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_DepositClients_Deposits_DepositId",
column: x => x.DepositId,
principalTable: "Deposits",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Replenishments",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Amount = table.Column<decimal>(type: "numeric", nullable: false),
Date = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DepositId = table.Column<string>(type: "text", nullable: false),
ClerkId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Replenishments", x => x.Id);
table.ForeignKey(
name: "FK_Replenishments_Clerks_ClerkId",
column: x => x.ClerkId,
principalTable: "Clerks",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Replenishments_Deposits_DepositId",
column: x => x.DepositId,
principalTable: "Deposits",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "DepositCurrencies",
columns: table => new
{
DepositId = table.Column<string>(type: "text", nullable: false),
CurrencyId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DepositCurrencies", x => new { x.DepositId, x.CurrencyId });
table.ForeignKey(
name: "FK_DepositCurrencies_Currencies_CurrencyId",
column: x => x.CurrencyId,
principalTable: "Currencies",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_DepositCurrencies_Deposits_DepositId",
column: x => x.DepositId,
principalTable: "Deposits",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "CreditPrograms",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Cost = table.Column<decimal>(type: "numeric", nullable: false),
MaxCost = table.Column<decimal>(type: "numeric", nullable: false),
StorekeeperId = table.Column<string>(type: "text", nullable: false),
PeriodId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CreditPrograms", x => x.Id);
table.ForeignKey(
name: "FK_CreditPrograms_Periods_PeriodId",
column: x => x.PeriodId,
principalTable: "Periods",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_CreditPrograms_Storekeepers_StorekeeperId",
column: x => x.StorekeeperId,
principalTable: "Storekeepers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "CreditProgramClients",
columns: table => new
{
ClientId = table.Column<string>(type: "text", nullable: false),
CreditProgramId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CreditProgramClients", x => new { x.ClientId, x.CreditProgramId });
table.ForeignKey(
name: "FK_CreditProgramClients_Clients_ClientId",
column: x => x.ClientId,
principalTable: "Clients",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_CreditProgramClients_CreditPrograms_CreditProgramId",
column: x => x.CreditProgramId,
principalTable: "CreditPrograms",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "CurrencyCreditPrograms",
columns: table => new
{
CreditProgramId = table.Column<string>(type: "text", nullable: false),
CurrencyId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CurrencyCreditPrograms", x => new { x.CreditProgramId, x.CurrencyId });
table.ForeignKey(
name: "FK_CurrencyCreditPrograms_CreditPrograms_CreditProgramId",
column: x => x.CreditProgramId,
principalTable: "CreditPrograms",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_CurrencyCreditPrograms_Currencies_CurrencyId",
column: x => x.CurrencyId,
principalTable: "Currencies",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Clerks_Email",
table: "Clerks",
column: "Email",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Clerks_Login",
table: "Clerks",
column: "Login",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Clerks_PhoneNumber",
table: "Clerks",
column: "PhoneNumber",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Clients_ClerkId",
table: "Clients",
column: "ClerkId");
migrationBuilder.CreateIndex(
name: "IX_CreditProgramClients_CreditProgramId",
table: "CreditProgramClients",
column: "CreditProgramId");
migrationBuilder.CreateIndex(
name: "IX_CreditPrograms_Name",
table: "CreditPrograms",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_CreditPrograms_PeriodId",
table: "CreditPrograms",
column: "PeriodId");
migrationBuilder.CreateIndex(
name: "IX_CreditPrograms_StorekeeperId",
table: "CreditPrograms",
column: "StorekeeperId");
migrationBuilder.CreateIndex(
name: "IX_Currencies_Abbreviation",
table: "Currencies",
column: "Abbreviation",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Currencies_StorekeeperId",
table: "Currencies",
column: "StorekeeperId");
migrationBuilder.CreateIndex(
name: "IX_CurrencyCreditPrograms_CurrencyId",
table: "CurrencyCreditPrograms",
column: "CurrencyId");
migrationBuilder.CreateIndex(
name: "IX_DepositClients_ClientId",
table: "DepositClients",
column: "ClientId");
migrationBuilder.CreateIndex(
name: "IX_DepositCurrencies_CurrencyId",
table: "DepositCurrencies",
column: "CurrencyId");
migrationBuilder.CreateIndex(
name: "IX_Deposits_ClerkId",
table: "Deposits",
column: "ClerkId");
migrationBuilder.CreateIndex(
name: "IX_Periods_StorekeeperId",
table: "Periods",
column: "StorekeeperId");
migrationBuilder.CreateIndex(
name: "IX_Replenishments_ClerkId",
table: "Replenishments",
column: "ClerkId");
migrationBuilder.CreateIndex(
name: "IX_Replenishments_DepositId",
table: "Replenishments",
column: "DepositId");
migrationBuilder.CreateIndex(
name: "IX_Storekeepers_Email",
table: "Storekeepers",
column: "Email",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Storekeepers_Login",
table: "Storekeepers",
column: "Login",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Storekeepers_PhoneNumber",
table: "Storekeepers",
column: "PhoneNumber",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CreditProgramClients");
migrationBuilder.DropTable(
name: "CurrencyCreditPrograms");
migrationBuilder.DropTable(
name: "DepositClients");
migrationBuilder.DropTable(
name: "DepositCurrencies");
migrationBuilder.DropTable(
name: "Replenishments");
migrationBuilder.DropTable(
name: "CreditPrograms");
migrationBuilder.DropTable(
name: "Clients");
migrationBuilder.DropTable(
name: "Currencies");
migrationBuilder.DropTable(
name: "Deposits");
migrationBuilder.DropTable(
name: "Periods");
migrationBuilder.DropTable(
name: "Clerks");
migrationBuilder.DropTable(
name: "Storekeepers");
}
}
}

View File

@@ -0,0 +1,559 @@
// <auto-generated />
using System;
using BankDatabase;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BankDatabase.Migrations
{
[DbContext(typeof(BankDbContext))]
partial class BankDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BankDatabase.Models.Clerk", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Login")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MiddleName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Surname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.HasIndex("Login")
.IsUnique();
b.HasIndex("PhoneNumber")
.IsUnique();
b.ToTable("Clerks");
});
modelBuilder.Entity("BankDatabase.Models.Client", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<decimal>("Balance")
.HasColumnType("numeric");
b.Property<string>("ClerkId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Surname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ClerkId");
b.ToTable("Clients");
});
modelBuilder.Entity("BankDatabase.Models.ClientCreditProgram", b =>
{
b.Property<string>("ClientId")
.HasColumnType("text");
b.Property<string>("CreditProgramId")
.HasColumnType("text");
b.HasKey("ClientId", "CreditProgramId");
b.HasIndex("CreditProgramId");
b.ToTable("CreditProgramClients");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgram", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<decimal>("Cost")
.HasColumnType("numeric");
b.Property<decimal>("MaxCost")
.HasColumnType("numeric");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PeriodId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StorekeeperId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.HasIndex("PeriodId");
b.HasIndex("StorekeeperId");
b.ToTable("CreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgramCurrency", b =>
{
b.Property<string>("CreditProgramId")
.HasColumnType("text");
b.Property<string>("CurrencyId")
.HasColumnType("text");
b.HasKey("CreditProgramId", "CurrencyId");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyCreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.Currency", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Abbreviation")
.IsRequired()
.HasColumnType("text");
b.Property<decimal>("Cost")
.HasColumnType("numeric");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StorekeeperId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Abbreviation")
.IsUnique();
b.HasIndex("StorekeeperId");
b.ToTable("Currencies");
});
modelBuilder.Entity("BankDatabase.Models.Deposit", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("ClerkId")
.IsRequired()
.HasColumnType("text");
b.Property<decimal>("Cost")
.HasColumnType("numeric");
b.Property<float>("InterestRate")
.HasColumnType("real");
b.Property<int>("Period")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ClerkId");
b.ToTable("Deposits");
});
modelBuilder.Entity("BankDatabase.Models.DepositClient", b =>
{
b.Property<string>("DepositId")
.HasColumnType("text");
b.Property<string>("ClientId")
.HasColumnType("text");
b.HasKey("DepositId", "ClientId");
b.HasIndex("ClientId");
b.ToTable("DepositClients");
});
modelBuilder.Entity("BankDatabase.Models.DepositCurrency", b =>
{
b.Property<string>("DepositId")
.HasColumnType("text");
b.Property<string>("CurrencyId")
.HasColumnType("text");
b.HasKey("DepositId", "CurrencyId");
b.HasIndex("CurrencyId");
b.ToTable("DepositCurrencies");
});
modelBuilder.Entity("BankDatabase.Models.Period", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("EndTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("StartTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("StorekeeperId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("StorekeeperId");
b.ToTable("Periods");
});
modelBuilder.Entity("BankDatabase.Models.Replenishment", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<decimal>("Amount")
.HasColumnType("numeric");
b.Property<string>("ClerkId")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<string>("DepositId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ClerkId");
b.HasIndex("DepositId");
b.ToTable("Replenishments");
});
modelBuilder.Entity("BankDatabase.Models.Storekeeper", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Login")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MiddleName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Surname")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.HasIndex("Login")
.IsUnique();
b.HasIndex("PhoneNumber")
.IsUnique();
b.ToTable("Storekeepers");
});
modelBuilder.Entity("BankDatabase.Models.Client", b =>
{
b.HasOne("BankDatabase.Models.Clerk", "Clerk")
.WithMany("Clients")
.HasForeignKey("ClerkId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Clerk");
});
modelBuilder.Entity("BankDatabase.Models.ClientCreditProgram", b =>
{
b.HasOne("BankDatabase.Models.Client", "Client")
.WithMany("CreditProgramClients")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.CreditProgram", "CreditProgram")
.WithMany("CreditProgramClients")
.HasForeignKey("CreditProgramId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("CreditProgram");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgram", b =>
{
b.HasOne("BankDatabase.Models.Period", "Period")
.WithMany("CreditPrograms")
.HasForeignKey("PeriodId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Storekeeper", "Storekeeper")
.WithMany("CreditPrograms")
.HasForeignKey("StorekeeperId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Period");
b.Navigation("Storekeeper");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgramCurrency", b =>
{
b.HasOne("BankDatabase.Models.CreditProgram", "CreditProgram")
.WithMany("CurrencyCreditPrograms")
.HasForeignKey("CreditProgramId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Currency", "Currency")
.WithMany("CurrencyCreditPrograms")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreditProgram");
b.Navigation("Currency");
});
modelBuilder.Entity("BankDatabase.Models.Currency", b =>
{
b.HasOne("BankDatabase.Models.Storekeeper", "Storekeeper")
.WithMany("Currencies")
.HasForeignKey("StorekeeperId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Storekeeper");
});
modelBuilder.Entity("BankDatabase.Models.Deposit", b =>
{
b.HasOne("BankDatabase.Models.Clerk", "Clerk")
.WithMany("Deposits")
.HasForeignKey("ClerkId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Clerk");
});
modelBuilder.Entity("BankDatabase.Models.DepositClient", b =>
{
b.HasOne("BankDatabase.Models.Client", "Client")
.WithMany("DepositClients")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Deposit", "Deposit")
.WithMany("DepositClients")
.HasForeignKey("DepositId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Client");
b.Navigation("Deposit");
});
modelBuilder.Entity("BankDatabase.Models.DepositCurrency", b =>
{
b.HasOne("BankDatabase.Models.Currency", "Currency")
.WithMany("DepositCurrencies")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("BankDatabase.Models.Deposit", "Deposit")
.WithMany("DepositCurrencies")
.HasForeignKey("DepositId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
b.Navigation("Deposit");
});
modelBuilder.Entity("BankDatabase.Models.Period", b =>
{
b.HasOne("BankDatabase.Models.Storekeeper", "Storekeeper")
.WithMany("Periods")
.HasForeignKey("StorekeeperId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Storekeeper");
});
modelBuilder.Entity("BankDatabase.Models.Replenishment", b =>
{
b.HasOne("BankDatabase.Models.Clerk", "Clerk")
.WithMany("Replenishments")
.HasForeignKey("ClerkId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("BankDatabase.Models.Deposit", "Deposit")
.WithMany("Replenishments")
.HasForeignKey("DepositId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Clerk");
b.Navigation("Deposit");
});
modelBuilder.Entity("BankDatabase.Models.Clerk", b =>
{
b.Navigation("Clients");
b.Navigation("Deposits");
b.Navigation("Replenishments");
});
modelBuilder.Entity("BankDatabase.Models.Client", b =>
{
b.Navigation("CreditProgramClients");
b.Navigation("DepositClients");
});
modelBuilder.Entity("BankDatabase.Models.CreditProgram", b =>
{
b.Navigation("CreditProgramClients");
b.Navigation("CurrencyCreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.Currency", b =>
{
b.Navigation("CurrencyCreditPrograms");
b.Navigation("DepositCurrencies");
});
modelBuilder.Entity("BankDatabase.Models.Deposit", b =>
{
b.Navigation("DepositClients");
b.Navigation("DepositCurrencies");
b.Navigation("Replenishments");
});
modelBuilder.Entity("BankDatabase.Models.Period", b =>
{
b.Navigation("CreditPrograms");
});
modelBuilder.Entity("BankDatabase.Models.Storekeeper", b =>
{
b.Navigation("CreditPrograms");
b.Navigation("Currencies");
b.Navigation("Periods");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class Clerk
public class Clerk
{
public required string Id { get; set; }

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class Client
public class Client
{
public required string Id { get; set; }

View File

@@ -1,6 +1,8 @@
namespace BankDatabase.Models;
using System.ComponentModel.DataAnnotations.Schema;
class ClientCreditProgram
namespace BankDatabase.Models;
public class ClientCreditProgram
{
public required string ClientId { get; set; }

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class CreditProgram
public class CreditProgram
{
public required string Id { get; set; }

View File

@@ -1,6 +1,8 @@
namespace BankDatabase.Models;
using System.ComponentModel.DataAnnotations.Schema;
class CreditProgramCurrency
namespace BankDatabase.Models;
public class CreditProgramCurrency
{
public required string CreditProgramId { get; set; }

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class Currency
public class Currency
{
public required string Id { get; set; }

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class Deposit
public class Deposit
{
public required string Id { get; set; }

View File

@@ -1,6 +1,8 @@
namespace BankDatabase.Models;
using System.ComponentModel.DataAnnotations.Schema;
class DepositClient
namespace BankDatabase.Models;
public class DepositClient
{
public required string DepositId { get; set; }

View File

@@ -1,6 +1,8 @@
namespace BankDatabase.Models;
using System.ComponentModel.DataAnnotations.Schema;
class DepositCurrency
namespace BankDatabase.Models;
public class DepositCurrency
{
public required string DepositId { get; set; }

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class Period
public class Period
{
public required string Id { get; set; }

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class Replenishment
public class Replenishment
{
public required string Id { get; set; }

View File

@@ -2,7 +2,7 @@
namespace BankDatabase.Models;
class Storekeeper
public class Storekeeper
{
public required string Id { get; set; }

View File

@@ -5,5 +5,5 @@ namespace BankTests.Infrastructure;
internal class ConfigurationDatabase : IConfigurationDatabase
{
public string ConnectionString =>
"Host=127.0.0.1;Port=5432;Database=TitanicTest;Username=postgres;Password=postgres;Include Error Detail=true";
"Host=127.0.0.1;Port=5432;Database=TitanicTest;Username=postgres;Password=admin123;Include Error Detail=true";
}

View File

@@ -7,6 +7,8 @@ using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.ViewModels;
using BankWebApi.Infrastructure;
using System.Buffers;
namespace BankWebApi.Adapters;
@@ -14,11 +16,13 @@ public class StorekeeperAdapter : IStorekeeperAdapter
{
private readonly IStorekeeperBusinessLogicContract _storekeeperBusinessLogicContract;
private readonly IJwtProvider _jwtProvider;
private readonly ILogger _logger;
private readonly Mapper _mapper;
public StorekeeperAdapter(IStorekeeperBusinessLogicContract storekeeperBusinessLogicContract, ILogger logger)
public StorekeeperAdapter(IStorekeeperBusinessLogicContract storekeeperBusinessLogicContract, IJwtProvider jwtProvider, ILogger logger)
{
_storekeeperBusinessLogicContract = storekeeperBusinessLogicContract;
_logger = logger;
@@ -28,6 +32,7 @@ public class StorekeeperAdapter : IStorekeeperAdapter
cfg.CreateMap<StorekeeperDataModel, StorekeeperViewModel>();
});
_mapper = new Mapper(config);
_jwtProvider = jwtProvider;
}
public StorekeeperOperationResponse GetList()
@@ -119,7 +124,7 @@ public class StorekeeperAdapter : IStorekeeperAdapter
{
_logger.LogError(ex, "StorageException");
return StorekeeperOperationResponse.BadRequest(
$"Error while working with data storage: {ex.InnerException!.Message}"
$"Error while working with data storage: {ex.InnerException?.Message}"
);
}
catch (Exception ex)
@@ -171,4 +176,30 @@ public class StorekeeperAdapter : IStorekeeperAdapter
return StorekeeperOperationResponse.InternalServerError(ex.Message);
}
}
public StorekeeperOperationResponse Login(StorekeeperAuthBindingModel storekeeperAuth, out string token)
{
token = string.Empty;
try
{
var storekeeper = _storekeeperBusinessLogicContract.GetStorekeeperByData(storekeeperAuth.Login);
var result = storekeeperAuth.Password == storekeeper.Password;
if (!result)
{
return StorekeeperOperationResponse.Unauthorized("Password are incorrect");
}
token = _jwtProvider.GenerateToken(storekeeper);
return StorekeeperOperationResponse.OK(token);
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception in Login");
return StorekeeperOperationResponse.InternalServerError($"Exception in Login {ex.Message}");
}
}
}

View File

@@ -9,6 +9,7 @@ namespace BankWebApi;
/// </summary>
public class AuthOptions
{
public const string CookieName = "bank";
public const string ISSUER = "Bank_AuthServer"; // издатель токена
public const string AUDIENCE = "Bank_AuthClient"; // потребитель токена
const string KEY = "banksuperpupersecret_secretsecretsecretkey!"; // ключ для шифрации

View File

@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />

View File

@@ -39,7 +39,8 @@ public class StorekeepersController(IStorekeeperAdapter adapter) : ControllerBas
/// </summary>
/// <param name="model">модель от пользователя</param>
/// <returns></returns>
[HttpPost]
[HttpPost("register")]
[AllowAnonymous]
public IActionResult Register([FromBody] StorekeeperBindingModel model)
{
return _adapter.RegisterStorekeeper(model).GetResponse(Request, Response);
@@ -55,4 +56,20 @@ public class StorekeepersController(IStorekeeperAdapter adapter) : ControllerBas
{
return _adapter.ChangeStorekeeperInfo(model).GetResponse(Request, Response);
}
/// <summary>
/// вход для кладовщика
/// </summary>
/// <param name="model">модель с логином и паролем</param>
/// <returns></returns>
[HttpPost("login")]
[AllowAnonymous]
public IActionResult Login([FromBody] StorekeeperAuthBindingModel model)
{
var res = _adapter.Login(model, out string token);
Response.Cookies.Append(AuthOptions.CookieName, token);
return res.GetResponse(Request, Response);
}
}

View File

@@ -0,0 +1,8 @@
using BankContracts.DataModels;
namespace BankWebApi.Infrastructure;
public interface IJwtProvider
{
string GenerateToken(StorekeeperDataModel dataModel);
}

View File

@@ -0,0 +1,21 @@
using BankContracts.DataModels;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
namespace BankWebApi.Infrastructure;
public class JwtProvider : IJwtProvider
{
public string GenerateToken(StorekeeperDataModel dataModel)
{
var token = new JwtSecurityToken(
issuer: AuthOptions.ISSUER,
audience: AuthOptions.AUDIENCE,
claims: [new("id", dataModel.Id)],
expires: DateTime.UtcNow.Add(TimeSpan.FromDays(2)),
signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}

View File

@@ -0,0 +1,7 @@
namespace BankWebApi.Infrastructure;
public class PasswordHelper
{
public static string HashPassword(string password) => BCrypt.Net.BCrypt.HashPassword(password);
public static bool VerifyPassword(string password, string hash) => BCrypt.Net.BCrypt.Verify(password, hash);
}

View File

@@ -28,12 +28,11 @@ builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Bank API", Version = "v1" });
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> XML-<2D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD>)
// Включение XML-комментариев (если они есть)
var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> JWT-<2D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> Swagger UI
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'",
@@ -78,6 +77,16 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
ValidateIssuerSigningKey = true,
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies[AuthOptions.CookieName];
return Task.CompletedTask;
}
};
});
@@ -87,13 +96,14 @@ builder.Services.AddCors(options =>
{
policy.WithOrigins("http://localhost:26312")
.AllowAnyMethod()
.AllowAnyHeader();
.AllowAnyHeader()
.AllowCredentials();
});
});
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
builder.Services.AddSingleton<IConfigurationDatabase, ConfigurationDatabase>();
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
builder.Services.AddSingleton<IConfigurationDatabase, BankWebApi.Infrastructure.ConfigurationDatabase>();
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
builder.Services.AddTransient<IClerkBusinessLogicContract, ClerkBusinessLogicContract>();
builder.Services.AddTransient<IPeriodBusinessLogicContract, PeriodBusinessLogicContract>();
builder.Services.AddTransient<IDepositBusinessLogicContract, DepositBusinessLogicContract>();
@@ -102,7 +112,7 @@ builder.Services.AddTransient<ICreditProgramBusinessLogicContract, CreditProgram
builder.Services.AddTransient<ICurrencyBusinessLogicContract, CurrencyBusinessLogicContract>();
builder.Services.AddTransient<IStorekeeperBusinessLogicContract, StorekeeperBusinessLogicContract>();
builder.Services.AddTransient<IReplenishmentBusinessLogicContract, ReplenishmentBusinessLogicContract>();
// <20><>
// <20><>
builder.Services.AddTransient<BankDbContext>();
builder.Services.AddTransient<IClerkStorageContract, ClerkStorageContract>();
builder.Services.AddTransient<IPeriodStorageContract, PeriodStorageContract>();
@@ -112,7 +122,7 @@ builder.Services.AddTransient<ICreditProgramStorageContract, CreditProgramStorag
builder.Services.AddTransient<ICurrencyStorageContract, CurrencyStorageContract>();
builder.Services.AddTransient<IStorekeeperStorageContract, StorekeeperStorageContract>();
builder.Services.AddTransient<IReplenishmentStorageContract, ReplenishmentStorageContract>();
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
builder.Services.AddTransient<IClerkAdapter, ClerkAdapter>();
builder.Services.AddTransient<IPeriodAdapter, PeriodAdapter>();
builder.Services.AddTransient<IDepositAdapter, DepositAdapter>();
@@ -121,6 +131,8 @@ builder.Services.AddTransient<ICreditProgramAdapter, CreditProgramAdapter>();
builder.Services.AddTransient<ICurrencyAdapter, CurrencyAdapter>();
builder.Services.AddTransient<IStorekeeperAdapter, StorekeeperAdapter>();
builder.Services.AddTransient<IReplenishmentAdapter, ReplenishmentAdapter>();
// shit
builder.Services.AddTransient<IJwtProvider, JwtProvider>();
var app = builder.Build();
@@ -132,7 +144,7 @@ if (app.Environment.IsDevelopment())
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Bank API V1");
c.RoutePrefix = "swagger"; // Swagger UI <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> /swagger
c.RoutePrefix = "swagger"; // Swagger UI <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> /swagger
});
}
if (app.Environment.IsProduction())
@@ -146,9 +158,17 @@ if (app.Environment.IsProduction())
}
app.UseCors("AllowFrontend");
app.UseHttpsRedirection();
app.UseCookiePolicy(new CookiePolicyOptions
{
HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always,
Secure = CookieSecurePolicy.Always
});
app.UseAuthentication();
app.UseAuthorization();
// это для тестов
app.Map("/login/{username}", (string username) =>
{
return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(

Binary file not shown.

3602
TheBank/bankui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -20,16 +20,19 @@
"@radix-ui/react-select": "^2.2.4",
"@radix-ui/react-separator": "^1.1.6",
"@radix-ui/react-slot": "^1.2.2",
"@radix-ui/react-tabs": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.6",
"@tailwindcss/vite": "^4.1.7",
"@tanstack/react-query": "^5.76.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.511.0",
"next-themes": "^0.4.6",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-hook-form": "^7.56.4",
"react-router-dom": "^7.6.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.3.0",
"tailwindcss": "^4.1.7",
"tw-animate-css": "^1.3.0",

View File

@@ -8,6 +8,7 @@ import type {
PeriodBindingModel,
ReplenishmentBindingModel,
StorekeeperBindingModel,
LoginBindingModel,
} from '../types/types';
// Clients API
@@ -117,4 +118,5 @@ export const storekeepersApi = {
postData('api/Storekeepers/Register', data),
update: (data: StorekeeperBindingModel) =>
putData('api/Storekeepers/ChangeInfo', data),
login: (data: LoginBindingModel) => postData('api/Storekeepers/login', data),
};

View File

@@ -7,6 +7,7 @@ export async function getData<T>(path: string): Promise<T[]> {
headers: {
mode: 'no-cors',
},
credentials: 'include',
});
if (!res.ok) {
throw new Error(`Не получается загрузить ${path}: ${res.statusText}`);
@@ -22,6 +23,7 @@ export async function postData<T>(path: string, data: T) {
'Content-Type': 'application/json',
mode: 'no-cors',
},
credentials: 'include',
body: JSON.stringify(data),
});
if (!res.ok) {
@@ -36,6 +38,7 @@ export async function putData<T>(path: string, data: T) {
'Content-Type': 'application/json',
mode: 'no-cors',
},
credentials: 'include',
body: JSON.stringify(data),
});
if (!res.ok) {

View File

@@ -0,0 +1,82 @@
import type { LoginBindingModel } from '@/types/types';
import { zodResolver } from '@hookform/resolvers/zod';
import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '../ui/form';
import { Input } from '../ui/input';
import { Button } from '../ui/button';
interface LoginFormProps {
onSubmit: (data: LoginBindingModel) => void;
defaultValues?: Partial<LoginBindingModel>;
}
const loginFormSchema = z.object({
login: z.string().min(3, 'Логин должен быть не короче 3 символов'),
password: z.string().min(6, 'Пароль должен быть не короче 6 символов'),
});
type FormValues = z.infer<typeof loginFormSchema>;
export const LoginForm = ({
onSubmit,
defaultValues,
}: LoginFormProps): React.JSX.Element => {
const form = useForm<FormValues>({
resolver: zodResolver(loginFormSchema),
defaultValues: {
login: defaultValues?.login || '',
password: defaultValues?.password || '',
},
});
const handleSubmit = (data: FormValues) => {
const payload: LoginBindingModel = {
...data,
};
onSubmit(payload);
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
<FormField
control={form.control}
name="login"
render={({ field }) => (
<FormItem>
<FormLabel>Логин</FormLabel>
<FormControl>
<Input placeholder="Логин" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Пароль</FormLabel>
<FormControl>
<Input type="password" placeholder="Пароль" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
Войти
</Button>
</form>
</Form>
);
};

View File

@@ -0,0 +1,166 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import type { StorekeeperBindingModel } from '@/types/types';
interface RegisterFormProps {
onSubmit: (data: StorekeeperBindingModel) => void;
defaultValues?: Partial<StorekeeperBindingModel>;
}
const registerFormSchema = z.object({
id: z.string().optional(),
name: z.string().min(1, 'Имя обязательно'),
surname: z.string().min(1, 'Фамилия обязательна'),
middleName: z.string().min(1, 'Отчество обязательно'),
login: z.string().min(3, 'Логин должен быть не короче 3 символов'),
password: z.string().min(6, 'Пароль должен быть не короче 6 символов'),
email: z.string().email('Введите корректный email'),
phoneNumber: z.string().min(10, 'Введите корректный номер телефона'),
});
type FormValues = z.infer<typeof registerFormSchema>;
export const RegisterForm = ({
onSubmit,
defaultValues,
}: RegisterFormProps): React.JSX.Element => {
const form = useForm<FormValues>({
resolver: zodResolver(registerFormSchema),
defaultValues: {
id: defaultValues?.id || crypto.randomUUID(),
name: defaultValues?.name || '',
surname: defaultValues?.surname || '',
middleName: defaultValues?.middleName || '',
login: defaultValues?.login || '',
password: defaultValues?.password || '',
email: defaultValues?.email || '',
phoneNumber: defaultValues?.phoneNumber || '',
},
});
const handleSubmit = (data: FormValues) => {
const payload: StorekeeperBindingModel = {
...data,
id: data.id || crypto.randomUUID(),
};
onSubmit(payload);
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
<FormField
control={form.control}
name="id"
render={({ field }) => <input type="hidden" {...field} />}
/>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Имя</FormLabel>
<FormControl>
<Input placeholder="Имя" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="surname"
render={({ field }) => (
<FormItem>
<FormLabel>Фамилия</FormLabel>
<FormControl>
<Input placeholder="Фамилия" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="middleName"
render={({ field }) => (
<FormItem>
<FormLabel>Отчество</FormLabel>
<FormControl>
<Input placeholder="Отчество" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="login"
render={({ field }) => (
<FormItem>
<FormLabel>Логин</FormLabel>
<FormControl>
<Input placeholder="Логин" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Пароль</FormLabel>
<FormControl>
<Input type="password" placeholder="Пароль" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="Email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel>Номер телефона</FormLabel>
<FormControl>
<Input placeholder="Номер телефона" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
Зарегистрировать
</Button>
</form>
</Form>
);
};

View File

@@ -15,7 +15,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '../ui/dropdown-menu';
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
import { Avatar, AvatarFallback } from '../ui/avatar';
import { Button } from '../ui/button';
type NavOptionValue = {

View File

@@ -0,0 +1,50 @@
import { useStorekeepers } from '@/hooks/useStorekeepers';
import React from 'react';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
import { RegisterForm } from '../features/RegisterForm';
import type { LoginBindingModel, StorekeeperBindingModel } from '@/types/types';
import { LoginForm } from '../features/LoginForm';
import { Toaster } from '../ui/sonner';
import { toast } from 'sonner';
export const AuthStorekeeper = (): React.JSX.Element => {
const { createStorekeeper, loginStorekeeper, isLoginError, loginError } =
useStorekeepers();
const handleRegister = (data: StorekeeperBindingModel) => {
console.log(data);
createStorekeeper(data);
};
const handleLogin = (data: LoginBindingModel) => {
console.log(data);
loginStorekeeper(data);
};
React.useEffect(() => {
if (isLoginError) {
toast(`Ошибка ${loginError?.message}`);
}
}, [isLoginError, loginError]);
return (
<>
<main className="flex flex-col justify-center items-center">
<div>
<Tabs defaultValue="login" className="w-[400px]">
<TabsList>
<TabsTrigger value="login">Вход</TabsTrigger>
<TabsTrigger value="register">Регистрация</TabsTrigger>
</TabsList>
<TabsContent value="login">
<LoginForm onSubmit={handleLogin} />
</TabsContent>
<TabsContent value="register">
<RegisterForm onSubmit={handleRegister} />
</TabsContent>
</Tabs>
</div>
</main>
<Toaster />
</>
);
};

View File

@@ -43,9 +43,8 @@ export const CreditPrograms = (): React.JSX.Element => {
isOpen={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
onSubmit={handleAdd}
>
<CreditProgramForm />
</DialogForm>
children={<CreditProgramForm />}
/>
<div className="">
<DataTable data={[]} columns={[]} />
</div>

View File

@@ -0,0 +1,23 @@
import { useTheme } from 'next-themes';
import { Toaster as Sonner, type ToasterProps } from 'sonner';
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = 'system' } = useTheme();
return (
<Sonner
theme={theme as ToasterProps['theme']}
className="toaster group"
style={
{
'--normal-bg': 'var(--popover)',
'--normal-text': 'var(--popover-foreground)',
'--normal-border': 'var(--border)',
} as React.CSSProperties
}
{...props}
/>
);
};
export { Toaster };

View File

@@ -0,0 +1,64 @@
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
import { cn } from "@/lib/utils"
function Tabs({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
className={cn("flex flex-col gap-2", className)}
{...props}
/>
)
}
function TabsList({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.List>) {
return (
<TabsPrimitive.List
data-slot="tabs-list"
className={cn(
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
className
)}
{...props}
/>
)
}
function TabsTrigger({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
<TabsPrimitive.Trigger
data-slot="tabs-trigger"
className={cn(
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function TabsContent({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
return (
<TabsPrimitive.Content
data-slot="tabs-content"
className={cn("flex-1 outline-none", className)}
{...props}
/>
)
}
export { Tabs, TabsList, TabsTrigger, TabsContent }

View File

@@ -7,33 +7,48 @@ export const useStorekeepers = () => {
const {
data: storekeepers,
isLoading,
isError,
isError: isGetAllError,
error,
} = useQuery({
queryKey: ['storekeepers'],
queryFn: storekeepersApi.getAll,
});
const { mutate: createStorekeeper } = useMutation({
const { mutate: createStorekeeper, isError: isCreateError } = useMutation({
mutationFn: storekeepersApi.create,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['storekeepers'] });
},
});
const { mutate: updateStorekeeper } = useMutation({
const { mutate: updateStorekeeper, isError: isUpdateError } = useMutation({
mutationFn: storekeepersApi.update,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['storekeepers'] });
},
});
const {
mutate: loginStorekeeper,
isError: isLoginError,
error: loginError,
} = useMutation({
mutationFn: storekeepersApi.login,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['storekeepers'] });
},
});
return {
storekeepers,
isLoading,
isError,
isGetAllError,
isCreateError,
isLoginError,
loginError,
error,
createStorekeeper,
loginStorekeeper,
updateStorekeeper,
};
};

View File

@@ -6,6 +6,7 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Currencies } from './components/pages/Currencies.tsx';
import { CreditPrograms } from './components/pages/CreditPrograms.tsx';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthStorekeeper } from './components/pages/AuthStorekeeper.tsx';
const routes = createBrowserRouter([
{
@@ -23,6 +24,10 @@ const routes = createBrowserRouter([
],
errorElement: <p>бля пизда рулям</p>,
},
{
path: '/auth',
element: <AuthStorekeeper />,
},
]);
const queryClient = new QueryClient();

View File

@@ -89,3 +89,8 @@ export interface StorekeeperBindingModel {
email?: string;
phoneNumber?: string;
}
export interface LoginBindingModel {
login: string;
password: string;
}