Migrate to EF.Core
This commit is contained in:
parent
d819bee048
commit
5f75f1ec71
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -187,3 +187,5 @@ Temporary Items
|
|||
.vscode/*
|
||||
|
||||
database.db
|
||||
database.db-shm
|
||||
database.db-wal
|
||||
|
|
|
@ -9,12 +9,15 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="linq2db" Version="4.4.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.6" />
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.115" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.osx.arm64" Version="1.0.117" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">
|
||||
|
|
|
@ -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<IDataProviderSettings> DataProviders => Enumerable.Empty<IDataProviderSettings>();
|
||||
|
||||
public string DefaultConfiguration => "SQLite";
|
||||
public string DefaultDataProvider => "SQLite";
|
||||
|
||||
public IEnumerable<IConnectionStringSettings> 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> DbInfo => this.GetTable<DbInfo>();
|
||||
public ITable<User> Users => this.GetTable<User>();
|
||||
public ITable<Card> Cards => this.GetTable<Card>();
|
||||
public ITable<Config> Config => this.GetTable<Config>();
|
||||
}
|
||||
}
|
21
AfRApay.Web/Backend/Database/DatabaseContext.cs
Normal file
21
AfRApay.Web/Backend/Database/DatabaseContext.cs
Normal file
|
@ -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<DatabaseContext> options) : base(options) { }
|
||||
|
||||
public virtual DbSet<Card> Cards { get; set; } = null!;
|
||||
public virtual DbSet<User> Users { get; set; } = null!;
|
||||
public virtual DbSet<Config> 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);
|
||||
}
|
95
AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.Designer.cs
generated
Normal file
95
AfRApay.Web/Backend/Database/Migrations/20230213181437_InitialCreate.Designer.cs
generated
Normal file
|
@ -0,0 +1,95 @@
|
|||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<string>("Id")
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("CardId");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Cards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Config", b =>
|
||||
{
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("Name");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("Value");
|
||||
|
||||
b.HasKey("Name");
|
||||
|
||||
b.ToTable("Config");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.User", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("ID");
|
||||
|
||||
b.Property<long>("Balance")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("Balance");
|
||||
|
||||
b.Property<string>("LastIdempotencyKey")
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("LastIdempotencyKey");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AfRApay.Web.Backend.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Config",
|
||||
columns: table => new
|
||||
{
|
||||
Name = table.Column<string>(type: "NVarChar(255)", nullable: false),
|
||||
Value = table.Column<string>(type: "NVarChar(255)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Config", x => x.Name);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<long>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Balance = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
Nickname = table.Column<string>(type: "NVarChar(255)", nullable: false),
|
||||
LastIdempotencyKey = table.Column<string>(type: "NVarChar(255)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.ID);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Cards",
|
||||
columns: table => new
|
||||
{
|
||||
CardId = table.Column<string>(type: "NVarChar(255)", nullable: false),
|
||||
UserId = table.Column<long>(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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Cards");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Config");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
// <auto-generated />
|
||||
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<string>("Id")
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("CardId");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Cards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.Config", b =>
|
||||
{
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("Name");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("Value");
|
||||
|
||||
b.HasKey("Name");
|
||||
|
||||
b.ToTable("Config");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AfRApay.Web.Backend.Database.Tables.User", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("ID");
|
||||
|
||||
b.Property<long>("Balance")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("Balance");
|
||||
|
||||
b.Property<string>("LastIdempotencyKey")
|
||||
.HasColumnType("NVarChar(255)")
|
||||
.HasColumnName("LastIdempotencyKey");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
10
AfRApay.Web/Backend/Database/Tables/Card.cs
Normal file
10
AfRApay.Web/Backend/Database/Tables/Card.cs
Normal file
|
@ -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!;
|
||||
}
|
10
AfRApay.Web/Backend/Database/Tables/Config.cs
Normal file
10
AfRApay.Web/Backend/Database/Tables/Config.cs
Normal file
|
@ -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!;
|
||||
}
|
14
AfRApay.Web/Backend/Database/Tables/User.cs
Normal file
14
AfRApay.Web/Backend/Database/Tables/User.cs
Normal file
|
@ -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; }
|
||||
}
|
|
@ -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<Migration> _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<DbInfo>();
|
||||
db.CreateTable<User>();
|
||||
db.CreateTable<Card>();
|
||||
db.CreateTable<Config>();
|
||||
|
||||
db.InsertWithIdentity(new DbInfo { DbVer = DbVer });
|
||||
}
|
||||
else if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) {
|
||||
db.CreateTable<DbInfo>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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<IActionResult> 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<IActionResult> 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<UserResponse> {
|
||||
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<ErrorResponse> {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.ComponentModel;
|
||||
using AfRApay.Web.Backend.Tables;
|
||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace AfRApay.Web.Controllers.Schema;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 @@
|
|||
<div class="row justify-content-center">
|
||||
@{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}")
|
||||
</td>
|
||||
<td>
|
||||
<b>@db.Cards.Count(p => p.UserId == user.Id)</b> cards linked.
|
||||
<b>@db.Cards.Count(p => p.User.Id == user.Id)</b> cards linked.
|
||||
</td>
|
||||
<td>
|
||||
<!-- Displayed when in big layout -->
|
||||
|
|
|
@ -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}");
|
||||
}
|
||||
|
|
|
@ -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://<yourip>: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.
|
||||
|
|
Loading…
Reference in a new issue