diff --git a/.gitignore b/.gitignore index ae4e4c7..341404f 100644 --- a/.gitignore +++ b/.gitignore @@ -187,3 +187,5 @@ Temporary Items .vscode/* database.db +database.db-shm +database.db-wal diff --git a/AfRApay.Web/AfRApay.Web.csproj b/AfRApay.Web/AfRApay.Web.csproj index cdaf342..0c26cd3 100644 --- a/AfRApay.Web/AfRApay.Web.csproj +++ b/AfRApay.Web/AfRApay.Web.csproj @@ -9,12 +9,15 @@ - + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - diff --git a/AfRApay.Web/Backend/Database.cs b/AfRApay.Web/Backend/Database.cs deleted file mode 100644 index afff74c..0000000 --- a/AfRApay.Web/Backend/Database.cs +++ /dev/null @@ -1,34 +0,0 @@ -using AfRApay.Web.Backend.Tables; -using LinqToDB; -using LinqToDB.Configuration; -using LinqToDB.Data; - -namespace AfRApay.Web.Backend; - -public class Database { - private class ConnectionStringSettings : IConnectionStringSettings { - public string ConnectionString { get; set; } = null!; - public string Name { get; set; } = null!; - public string ProviderName { get; set; } = null!; - public bool IsGlobal => false; - } - - public class Settings : ILinqToDBSettings { - public IEnumerable DataProviders => Enumerable.Empty(); - - public string DefaultConfiguration => "SQLite"; - public string DefaultDataProvider => "SQLite"; - - public IEnumerable ConnectionStrings { - get { yield return new ConnectionStringSettings { Name = "db", ProviderName = "SQLite", ConnectionString = @"Data Source=database.db;" }; } - } - } - - public class DbConn : DataConnection { - public DbConn() : base("db") { } - public ITable DbInfo => this.GetTable(); - public ITable Users => this.GetTable(); - public ITable Cards => this.GetTable(); - public ITable Config => this.GetTable(); - } -} diff --git a/AfRApay.Web/Backend/Database/DatabaseContext.cs b/AfRApay.Web/Backend/Database/DatabaseContext.cs new file mode 100644 index 0000000..79a9c39 --- /dev/null +++ b/AfRApay.Web/Backend/Database/DatabaseContext.cs @@ -0,0 +1,21 @@ +using AfRApay.Web.Backend.Database.Tables; +using Microsoft.EntityFrameworkCore; + +namespace AfRApay.Web.Backend.Database; + +public partial class DatabaseContext : DbContext { + public DatabaseContext() { } + public DatabaseContext(DbContextOptions options) : base(options) { } + + public virtual DbSet Cards { get; set; } = null!; + public virtual DbSet Users { get; set; } = null!; + public virtual DbSet Config { get; set; } = null!; + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlite("Data Source=database.db"); + + protected override void OnModelCreating(ModelBuilder modelBuilder) { + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} diff --git a/AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.Designer.cs b/AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.Designer.cs new file mode 100644 index 0000000..aa87f38 --- /dev/null +++ b/AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.Designer.cs @@ -0,0 +1,95 @@ +// +using AfRApay.Web.Backend.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AfRApay.Web.Backend.Database.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20230213181437_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.2"); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b => + { + b.Property("Id") + .HasColumnType("NVarChar(255)") + .HasColumnName("CardId"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Cards"); + }); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Config", b => + { + b.Property("Name") + .HasColumnType("NVarChar(255)") + .HasColumnName("Name"); + + b.Property("Value") + .IsRequired() + .HasColumnType("NVarChar(255)") + .HasColumnName("Value"); + + b.HasKey("Name"); + + b.ToTable("Config"); + }); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ID"); + + b.Property("Balance") + .HasColumnType("INTEGER") + .HasColumnName("Balance"); + + b.Property("LastIdempotencyKey") + .HasColumnType("NVarChar(255)") + .HasColumnName("LastIdempotencyKey"); + + b.Property("Nickname") + .IsRequired() + .HasColumnType("NVarChar(255)") + .HasColumnName("Nickname"); + + b.HasKey("Id"); + + b.HasIndex("Nickname") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b => + { + b.HasOne("AfRApay.Web.Backend.Database.Tables.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.cs b/AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.cs new file mode 100644 index 0000000..e2d8a0c --- /dev/null +++ b/AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.cs @@ -0,0 +1,83 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AfRApay.Web.Backend.Database.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Config", + columns: table => new + { + Name = table.Column(type: "NVarChar(255)", nullable: false), + Value = table.Column(type: "NVarChar(255)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Config", x => x.Name); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Balance = table.Column(type: "INTEGER", nullable: false), + Nickname = table.Column(type: "NVarChar(255)", nullable: false), + LastIdempotencyKey = table.Column(type: "NVarChar(255)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "Cards", + columns: table => new + { + CardId = table.Column(type: "NVarChar(255)", nullable: false), + UserId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Cards", x => x.CardId); + table.ForeignKey( + name: "FK_Cards_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Cards_UserId", + table: "Cards", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Users_Nickname", + table: "Users", + column: "Nickname", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Cards"); + + migrationBuilder.DropTable( + name: "Config"); + + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/AfRApay.Web/Backend/Database/Migrations/DatabaseContextModelSnapshot.cs b/AfRApay.Web/Backend/Database/Migrations/DatabaseContextModelSnapshot.cs new file mode 100644 index 0000000..b633f02 --- /dev/null +++ b/AfRApay.Web/Backend/Database/Migrations/DatabaseContextModelSnapshot.cs @@ -0,0 +1,92 @@ +// +using AfRApay.Web.Backend.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AfRApay.Web.Backend.Database.Migrations +{ + [DbContext(typeof(DatabaseContext))] + partial class DatabaseContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.2"); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b => + { + b.Property("Id") + .HasColumnType("NVarChar(255)") + .HasColumnName("CardId"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Cards"); + }); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Config", b => + { + b.Property("Name") + .HasColumnType("NVarChar(255)") + .HasColumnName("Name"); + + b.Property("Value") + .IsRequired() + .HasColumnType("NVarChar(255)") + .HasColumnName("Value"); + + b.HasKey("Name"); + + b.ToTable("Config"); + }); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ID"); + + b.Property("Balance") + .HasColumnType("INTEGER") + .HasColumnName("Balance"); + + b.Property("LastIdempotencyKey") + .HasColumnType("NVarChar(255)") + .HasColumnName("LastIdempotencyKey"); + + b.Property("Nickname") + .IsRequired() + .HasColumnType("NVarChar(255)") + .HasColumnName("Nickname"); + + b.HasKey("Id"); + + b.HasIndex("Nickname") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Card", b => + { + b.HasOne("AfRApay.Web.Backend.Database.Tables.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AfRApay.Web/Backend/Database/Tables/Card.cs b/AfRApay.Web/Backend/Database/Tables/Card.cs new file mode 100644 index 0000000..20bb8ad --- /dev/null +++ b/AfRApay.Web/Backend/Database/Tables/Card.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AfRApay.Web.Backend.Database.Tables; + +[Table("Cards")] +public class Card { + [Column("CardId", TypeName = "NVarChar(255)")] [Key] public string Id { get; set; } = null!; + [Column("UserId")] [Required] public User User { get; set; } = null!; +} diff --git a/AfRApay.Web/Backend/Database/Tables/Config.cs b/AfRApay.Web/Backend/Database/Tables/Config.cs new file mode 100644 index 0000000..2ac1d65 --- /dev/null +++ b/AfRApay.Web/Backend/Database/Tables/Config.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AfRApay.Web.Backend.Database.Tables; + +[Table("Config")] +public class Config { + [Column("Name", TypeName = "NVarChar(255)")] [Key] public string Name { get; set; } = null!; + [Column("Value", TypeName = "NVarChar(255)")] [Required] public string Value { get; set; } = null!; +} diff --git a/AfRApay.Web/Backend/Database/Tables/User.cs b/AfRApay.Web/Backend/Database/Tables/User.cs new file mode 100644 index 0000000..efe843c --- /dev/null +++ b/AfRApay.Web/Backend/Database/Tables/User.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace AfRApay.Web.Backend.Database.Tables; + +[Table("Users")] +[Index(nameof(Nickname), IsUnique = true)] +public class User { + [Column("ID")] [Key] public long Id { get; set; } + [Column("Balance")] [Required] public long Balance { get; set; } + [Column("Nickname", TypeName = "NVarChar(255)")] [Required] public string Nickname { get; set; } = null!; + [Column("LastIdempotencyKey", TypeName = "NVarChar(255)")] public string? LastIdempotencyKey { get; set; } +} diff --git a/AfRApay.Web/Backend/Migrations.cs b/AfRApay.Web/Backend/Migrations.cs deleted file mode 100644 index dae334b..0000000 --- a/AfRApay.Web/Backend/Migrations.cs +++ /dev/null @@ -1,91 +0,0 @@ -using AfRApay.Web.Backend.Tables; -using LinqToDB; -using LinqToDB.Data; - -namespace AfRApay.Web.Backend; - -public static class Migrations { - private const int DbVer = 1; - - // ReSharper disable once CollectionNeverUpdated.Local - // ReSharper disable once InconsistentNaming - private static readonly List _migrations = new(); - - public static void RunMigrations() { - using var db = new Database.DbConn(); - var ccolor = Console.ForegroundColor; - - if (!db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.Any()) { - Console.ForegroundColor = ConsoleColor.DarkCyan; - Console.Write("Running migration: "); - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("Initialize Database"); - - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - db.CreateTable(); - - db.InsertWithIdentity(new DbInfo { DbVer = DbVer }); - } - else if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) { - db.CreateTable(); - db.InsertWithIdentity(new DbInfo { DbVer = 0 }); - } - - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"Database version: {db.DbInfo.ToList().First().DbVer}"); - - var migrationsToRun = _migrations.FindAll(p => p.IntroducedWithDbVer > db.DbInfo.First().DbVer); - if (migrationsToRun.Count == 0) { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("No migrations to run."); - } - else { - new Migration(0, "BEGIN TRANSACTION").Run(db); - try { - migrationsToRun.ForEach(p => p.Run(db)); - } - catch { - Console.ForegroundColor = ConsoleColor.DarkRed; - Console.WriteLine($"Migrating to database version {DbVer} failed."); - new Migration(0, "ROLLBACK TRANSACTION").Run(db); - Console.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine("Rolled back migrations."); - Environment.Exit(1); - } - - new Migration(0, "COMMIT TRANSACTION").Run(db); - - var newdb = new Database.DbConn(); - var dbinfo = newdb.DbInfo.First(); - dbinfo.DbVer = DbVer; - newdb.Update(dbinfo); - - Console.ForegroundColor = ConsoleColor.DarkGreen; - Console.WriteLine($"Database version is now: {DbVer}"); - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("Finished running migrations."); - } - - Console.ForegroundColor = ccolor; - } - - private class Migration { - private readonly string _sql; - public readonly int IntroducedWithDbVer; - - public Migration(int introducedWithDbVer, string sql) { - IntroducedWithDbVer = introducedWithDbVer; - _sql = sql; - } - - public void Run(DataConnection db) { - Console.ForegroundColor = ConsoleColor.DarkCyan; - Console.Write("Running migration: "); - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine(_sql); - db.Execute(_sql); - } - } -} diff --git a/AfRApay.Web/Backend/Startup.cs b/AfRApay.Web/Backend/Startup.cs index 8e97457..4a8ec63 100644 --- a/AfRApay.Web/Backend/Startup.cs +++ b/AfRApay.Web/Backend/Startup.cs @@ -1,11 +1,8 @@ using System.Reflection; -using AfRApay.Web.Backend; -using LinqToDB.Data; +using AfRApay.Web.Backend.Database; +using Microsoft.EntityFrameworkCore; using Swashbuckle.AspNetCore.Filters; -DataConnection.DefaultSettings = new Database.Settings(); -Migrations.RunMigrations(); - var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); @@ -17,6 +14,7 @@ builder.Services.AddSwaggerGen(options => { builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly()); #if (DEBUG) +builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddControllers().AddRazorRuntimeCompilation(); builder.Services.AddControllers(); #else @@ -40,4 +38,6 @@ app.UseAuthorization(); app.MapRazorPages(); app.MapControllers(); +new DatabaseContext().Database.Migrate(); + app.Run(); diff --git a/AfRApay.Web/Backend/Tables/Card.cs b/AfRApay.Web/Backend/Tables/Card.cs deleted file mode 100644 index 40e9332..0000000 --- a/AfRApay.Web/Backend/Tables/Card.cs +++ /dev/null @@ -1,11 +0,0 @@ -using LinqToDB.Mapping; - -#pragma warning disable CS8618 - -namespace AfRApay.Web.Backend.Tables; - -[Table(Name = "Cards")] -public class Card { - [Column(Name = "CardId"), PrimaryKey, NotNull] public string CardId { get; set; } - [Column(Name = "UserId"), NotNull] public int UserId { get; set; } -} diff --git a/AfRApay.Web/Backend/Tables/Config.cs b/AfRApay.Web/Backend/Tables/Config.cs deleted file mode 100644 index fe07891..0000000 --- a/AfRApay.Web/Backend/Tables/Config.cs +++ /dev/null @@ -1,10 +0,0 @@ -using LinqToDB.Mapping; -#pragma warning disable CS8618 - -namespace AfRApay.Web.Backend.Tables; - -[Table(Name = "Config")] -public class Config { - [Column(Name = "Name"), PrimaryKey, NotNull] public string Name { get; set; } - [Column(Name = "Value"), NotNull] public string Value { get; set; } -} diff --git a/AfRApay.Web/Backend/Tables/DbInfo.cs b/AfRApay.Web/Backend/Tables/DbInfo.cs deleted file mode 100644 index f70033b..0000000 --- a/AfRApay.Web/Backend/Tables/DbInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using LinqToDB.Mapping; - -namespace AfRApay.Web.Backend.Tables; - -[Table(Name = "DbInfo")] -public class DbInfo { - [Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; } - [Column(Name = "DbVer"), NotNull] public int DbVer { get; set; } -} diff --git a/AfRApay.Web/Backend/Tables/User.cs b/AfRApay.Web/Backend/Tables/User.cs deleted file mode 100644 index 40c752a..0000000 --- a/AfRApay.Web/Backend/Tables/User.cs +++ /dev/null @@ -1,13 +0,0 @@ -using LinqToDB.Mapping; - -#pragma warning disable CS8618 - -namespace AfRApay.Web.Backend.Tables; - -[Table(Name = "Users")] -public class User { - [Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; } - [Column(Name = "Nickname"), NotNull] public string Nickname { get; set; } - [Column(Name = "Balance")] public int Balance { get; set; } - [Column(Name = "LastIdempotencyKey")] public string? LastIdempotencyKey { get; set; } -} diff --git a/AfRApay.Web/Controllers/CardController.cs b/AfRApay.Web/Controllers/CardController.cs index 0bc48d2..25ab3e5 100644 --- a/AfRApay.Web/Controllers/CardController.cs +++ b/AfRApay.Web/Controllers/CardController.cs @@ -1,8 +1,8 @@ -using AfRApay.Web.Backend; -using AfRApay.Web.Backend.Tables; +using AfRApay.Web.Backend.Database; +using AfRApay.Web.Backend.Database.Tables; using AfRApay.Web.Controllers.Schema; -using LinqToDB; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using Swashbuckle.AspNetCore.Filters; namespace AfRApay.Web.Controllers; @@ -25,8 +25,8 @@ public class CardController : Controller { [SwaggerResponseExample(StatusCodes.Status404NotFound, typeof(ErrorNoActiveLinkProcessExample))] [Route("/api/card/{card}/link")] public async Task Link(string card) { - var db = new Database.DbConn(); - if (db.Cards.Any(p => p.CardId == card)) { + var db = new DatabaseContext(); + if (db.Cards.Any(p => p.Id == card)) { return StatusCode(StatusCodes.Status304NotModified); } @@ -48,8 +48,8 @@ public class CardController : Controller { var user = db.Users.First(p => p.Id == int.Parse(linkFlag.Value)); linkFlag.Value = ""; - await db.InsertAsync(new Card { CardId = card, UserId = user.Id }); - await db.UpdateAsync(linkFlag); + db.Add(new Card { Id = card, User = user }); + await db.SaveChangesAsync(); return Ok(new UserResponse(user)); } @@ -72,10 +72,9 @@ public class CardController : Controller { [SwaggerResponseExample(412, typeof(ErrorBalanceOutOfRangeExample))] [Route("/api/card/{card}/transaction/{ik}")] public async Task Transaction(string card, string ik, [FromQuery] int amount) { - var db = new Database.DbConn(); - if (db.Cards.Any(p => p.CardId == card)) { - var userId = db.Cards.First(p => p.CardId == card).UserId; - var user = db.Users.First(p => p.Id == userId); + var db = new DatabaseContext(); + if (db.Cards.Any(p => p.Id == card)) { + var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User; if (ik == "" || ik != user.LastIdempotencyKey) { user.LastIdempotencyKey = ik; @@ -87,7 +86,7 @@ public class CardController : Controller { user.Balance += amount; } - await db.UpdateAsync(user); + await db.SaveChangesAsync(); return Ok(new UserResponse(user)); } @@ -108,10 +107,9 @@ public class CardController : Controller { [SwaggerResponseExample(404, typeof(ErrorUnknownCardExample))] [Route("/api/card/{card}/balance")] public IActionResult Balance(string card) { - var db = new Database.DbConn(); - if (db.Cards.Any(p => p.CardId == card)) { - var userId = db.Cards.First(p => p.CardId == card).UserId; - var user = db.Users.First(p => p.Id == userId); + var db = new DatabaseContext(); + if (db.Cards.Any(p => p.Id == card)) { + var user = db.Cards.Include(p => p.User).First(p => p.Id == card).User; return Ok(new UserResponse(user)); } @@ -120,7 +118,7 @@ public class CardController : Controller { } private class UserUpdatedExample : IExamplesProvider { - public UserResponse GetExamples() => new(new User { Id = 123, Nickname = "testman", Balance = 2550, LastIdempotencyKey = "5a6c94aa"}); + public UserResponse GetExamples() => new(new User { Id = 123, Nickname = "testman", Balance = 2550, LastIdempotencyKey = "5a6c94aa" }); } private class ErrorUnknownCardExample : IExamplesProvider { diff --git a/AfRApay.Web/Controllers/Schema/ErrorResponse.cs b/AfRApay.Web/Controllers/Schema/ErrorResponse.cs index ae5dd44..26359db 100644 --- a/AfRApay.Web/Controllers/Schema/ErrorResponse.cs +++ b/AfRApay.Web/Controllers/Schema/ErrorResponse.cs @@ -1,5 +1,4 @@ using System.ComponentModel; -using AfRApay.Web.Backend.Tables; using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; namespace AfRApay.Web.Controllers.Schema; diff --git a/AfRApay.Web/Controllers/Schema/UserResponse.cs b/AfRApay.Web/Controllers/Schema/UserResponse.cs index 51b3a15..b1ee8ea 100644 --- a/AfRApay.Web/Controllers/Schema/UserResponse.cs +++ b/AfRApay.Web/Controllers/Schema/UserResponse.cs @@ -1,5 +1,5 @@ using System.ComponentModel; -using AfRApay.Web.Backend.Tables; +using AfRApay.Web.Backend.Database.Tables; using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; namespace AfRApay.Web.Controllers.Schema; diff --git a/AfRApay.Web/Pages/AddUser.cshtml.cs b/AfRApay.Web/Pages/AddUser.cshtml.cs index 7ea531a..b65de2f 100644 --- a/AfRApay.Web/Pages/AddUser.cshtml.cs +++ b/AfRApay.Web/Pages/AddUser.cshtml.cs @@ -1,8 +1,6 @@ -using System.Data; -using System.Net; -using AfRApay.Web.Backend; -using AfRApay.Web.Backend.Tables; -using LinqToDB; +using System.Net; +using AfRApay.Web.Backend.Database; +using AfRApay.Web.Backend.Database.Tables; using Microsoft.AspNetCore.Mvc.RazorPages; namespace AfRApay.Web.Pages; @@ -10,17 +8,18 @@ namespace AfRApay.Web.Pages; public class AddUserModel : PageModel { public void OnGet() { } - public void OnPost() { - using var db = new Database.DbConn(); + public async void OnPost() { + await using var db = new DatabaseContext(); if (Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) { var nick = Request.Form["nickname"]; - if (db.Users.Any(p => p.Nickname == nick)) { + if (db.Users.Any(p => p.Nickname == nick.ToString())) { Response.Redirect("/ErrorRedirect?redir=/AddUser&message=" + WebUtility.UrlEncode("User with nick already exists.")); return; } - var user = new User { Nickname = nick!, Balance = 0 }; - db.InsertWithIdentity(user); + var user = new User { Nickname = nick.ToString(), Balance = 0 }; + db.Add(user); + await db.SaveChangesAsync(); Response.Redirect($"/#{user.Nickname}"); return; } diff --git a/AfRApay.Web/Pages/EditUser.cshtml b/AfRApay.Web/Pages/EditUser.cshtml index 5f9fb47..e2ee3a3 100644 --- a/AfRApay.Web/Pages/EditUser.cshtml +++ b/AfRApay.Web/Pages/EditUser.cshtml @@ -1,15 +1,16 @@ @page "{id:int}" -@using AfRApay.Web.Backend +@using AfRApay.Web.Backend.Database @using Microsoft.AspNetCore.Mvc.TagHelpers +@using Microsoft.EntityFrameworkCore @model EditUserModel @{ ViewData["Title"] = "Edit User"; if (Request.Method == "POST" && Request.Form["action"] == "delete") { return; } - var db = new Database.DbConn(); + var db = new DatabaseContext(); var user = db.Users.First(p => p.Id == int.Parse(RouteData.Values["id"]!.ToString()!)); - var cards = db.Cards.Where(p => p.UserId == user.Id); + var cards = db.Cards.Include(p => p.User).Where(p => p.User == user); var linkFlag = db.Config.FirstOrDefault(p => p.Name == "link"); var lTimeFlag = db.Config.FirstOrDefault(p => p.Name == "lTime"); @@ -142,8 +143,8 @@
@{ foreach (var card in cards) { - await RenderCard(user.Nickname, card.CardId); - await RenderCardModal(user.Nickname, card.CardId); + await RenderCard(user.Nickname, card.Id); + await RenderCardModal(user.Nickname, card.Id); } await RenderCard(user.Nickname, type: EditUserModel.CardType.LinkPlaceholder); } diff --git a/AfRApay.Web/Pages/EditUser.cshtml.cs b/AfRApay.Web/Pages/EditUser.cshtml.cs index 81d63a3..ca02a1f 100644 --- a/AfRApay.Web/Pages/EditUser.cshtml.cs +++ b/AfRApay.Web/Pages/EditUser.cshtml.cs @@ -1,8 +1,6 @@ -using System.Data; -using System.Net; -using AfRApay.Web.Backend; -using AfRApay.Web.Backend.Tables; -using LinqToDB; +using System.Net; +using AfRApay.Web.Backend.Database; +using AfRApay.Web.Backend.Database.Tables; using Microsoft.AspNetCore.Mvc.RazorPages; namespace AfRApay.Web.Pages; @@ -10,21 +8,22 @@ namespace AfRApay.Web.Pages; public class EditUserModel : PageModel { public void OnGet() { } - public void OnPost() { - using var db = new Database.DbConn(); - var userId = int.Parse(RouteData.Values["id"]!.ToString()!); - var user = db.Users.First(p => p.Id == userId); + public async void OnPost() { + await using var db = new DatabaseContext(); + var userId = long.Parse(RouteData.Values["id"]!.ToString()!); + var user = db.Users.First(p => p.Id == userId); if (Request.Form["action"] == "delete") { - db.Cards.Delete(p => p.UserId == userId); - db.Delete(user); + db.Remove(user); + await db.SaveChangesAsync(); Response.Redirect("/"); return; } if (Request.Form["action"] == "deleteCard" && Request.Form.ContainsKey("cardId")) { - var card = db.Cards.First(p => p.CardId == Request.Form["cardId"]); - db.Delete(card); + var card = db.Cards.First(p => p.Id == Request.Form["cardId"].ToString()); + db.Remove(card); + await db.SaveChangesAsync(); Response.Redirect($"/EditUser/{userId}"); return; } @@ -37,16 +36,18 @@ public class EditUserModel : PageModel { if (lTimeFlag == null) { lTimeFlag = new Config { Name = "lTime", Value = DateTime.UtcNow.ToString("s") }; - db.Insert(lTimeFlag); + db.Add(lTimeFlag); + await db.SaveChangesAsync(); } else { lTimeFlag.Value = DateTime.UtcNow.ToString("s"); - db.Update(lTimeFlag); + await db.SaveChangesAsync(); } if (linkFlag == null) { linkFlag = new Config { Name = "link", Value = user.Id.ToString() }; - db.Insert(linkFlag); + db.Add(linkFlag); + await db.SaveChangesAsync(); return; } @@ -55,27 +56,27 @@ public class EditUserModel : PageModel { } linkFlag.Value = user.Id.ToString(); - db.Update(linkFlag); + await db.SaveChangesAsync(); return; } if (Request.Form["action"] == "save" && Request.Form.ContainsKey("nickname") && !string.IsNullOrWhiteSpace(Request.Form["nickname"])) { - var nick = Request.Form["nickname"]; + var nick = Request.Form["nickname"].ToString(); if (db.Users.Any(p => p.Nickname == nick && p.Id != userId)) { Response.Redirect($"/ErrorRedirect?redir=/EditUser/{userId}&message=" + WebUtility.UrlEncode("User with nick already exists.")); return; } - user.Nickname = nick!; - db.Update(user); + user.Nickname = nick; + await db.SaveChangesAsync(); } Response.Redirect($"/#{user.Nickname}"); } public enum CardType { - Normal = 1, - LinkPlaceholder = 2, + Normal = 1, + LinkPlaceholder = 2, DeletionConfirmation = 3 } } diff --git a/AfRApay.Web/Pages/Index.cshtml b/AfRApay.Web/Pages/Index.cshtml index d8d5d37..d9a8a0c 100644 --- a/AfRApay.Web/Pages/Index.cshtml +++ b/AfRApay.Web/Pages/Index.cshtml @@ -1,9 +1,9 @@ @page -@using AfRApay.Web.Backend +@using AfRApay.Web.Backend.Database @model IndexModel @{ ViewData["Title"] = "Index"; - var db = new Database.DbConn(); + var db = new DatabaseContext(); } @section Header { @@ -62,7 +62,7 @@ @($"{user.Balance / 100M:C}") - @db.Cards.Count(p => p.UserId == user.Id) cards linked. + @db.Cards.Count(p => p.User.Id == user.Id) cards linked. diff --git a/AfRApay.Web/Pages/Index.cshtml.cs b/AfRApay.Web/Pages/Index.cshtml.cs index 5135ac3..8ab7456 100644 --- a/AfRApay.Web/Pages/Index.cshtml.cs +++ b/AfRApay.Web/Pages/Index.cshtml.cs @@ -1,7 +1,5 @@ -using System.Data; -using System.Net; -using AfRApay.Web.Backend; -using LinqToDB; +using System.Net; +using AfRApay.Web.Backend.Database; using Microsoft.AspNetCore.Mvc.RazorPages; namespace AfRApay.Web.Pages; @@ -9,8 +7,8 @@ namespace AfRApay.Web.Pages; public class IndexModel : PageModel { public void OnGet() { } - public void OnPost() { - using var db = new Database.DbConn(); + public async void OnPost() { + await using var db = new DatabaseContext(); if (Request.Form["action"] == "transaction" && Request.Form.ContainsKey("userId") && Request.Form.ContainsKey("amount")) { var userId = int.Parse(Request.Form["userId"].ToString()); var amount = int.Parse(Request.Form["amount"].ToString()); @@ -25,7 +23,7 @@ public class IndexModel : PageModel { } user.Balance += amount; - db.Update(user); + await db.SaveChangesAsync(); Response.Redirect($"/#{user.Nickname}"); } diff --git a/AfRApay.Web/README.md b/AfRApay.Web/README.md index f85e794..203b91f 100644 --- a/AfRApay.Web/README.md +++ b/AfRApay.Web/README.md @@ -4,6 +4,10 @@ This is the software that runs on the MateCard terminal, powered by an ESP32. ## Setting up dev environment This project uses PlatformIO. Quick setup guide: - Install dotnet-sdk (macOS: `brew install dotnet-sdk` / Arch Linux: `pacman -S dotnet-host dotnet-runtime dotnet-sdk dotnet-targeting-pack aspnet-runtime aspnet-targeting-pack`) -- To start the webserver, execute `dotnet run` in this directory. +- Install the EF.Core tool for database management: `dotnet tool install --global dotnet-ef` +- To start the webserver, execute `dotnet run` in this directory. If the database does not exist, it will be created. Migrations are applied automatically. - Swagger / OpenAPI docs will automatically generate at `/swagger`. - To test with the MateCard ESP32 code, get your computer's IP and configure the ESP to use `http://:5296` as the API base url. + +## Creating migrations +- To create a migration, change the respective code in `Backend/Database/Tables/TableName.cs` and then run `dotnet ef migrations add DescriptiveNameOfChanges`. The migration will be generated automatically.