Add EF migrations for the game and meta schema
This commit is contained in:
parent
6241225785
commit
f702d11ae4
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "10.0.4",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,6 +184,7 @@
|
|||
- [x] Таймер хода: 90 с + банк 5 мин.
|
||||
- [x] Снапшот матча в PostgreSQL + восстановление grain после рестарта.
|
||||
— заголовок + журнал интентов, реплей команд при старте.
|
||||
- [x] EF-миграции общей схемы (матчи + мета), `Migrate` при старте; InMemory — `EnsureCreated`.
|
||||
- [x] Redis: одноразовые тикеты и очередь.
|
||||
- [x] Idempotency по `intentId`.
|
||||
- [x] HTTP-очередь и тикеты: `docs/server-api.md`.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
| PostgreSQL (матчи), Redis (тикеты, очередь), Docker | готово |
|
||||
| Мета-API: JWT, коллекция, колоды, магазин, история, реплеи | готово |
|
||||
| CI (Forgejo Actions) | workflow добавлен, runner нужно привязать |
|
||||
| Orleans и миграции | не начато |
|
||||
| EF-миграции | готово (`InitialCreate`) |
|
||||
| Orleans | не начато |
|
||||
| Клиент и лаунчер | не начато |
|
||||
|
||||
## Требования
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
|
|||
принятых команд на детерминированном движке, включая ожидания выбора:
|
||||
делегаты движка не сериализуются. Журнал реплея сверяется по `eventSeq`.
|
||||
Банк и дедлайн пишутся каждые ~5 с; потеря при аварии — не больше тика.
|
||||
Схема — EF-миграции (`Migrations/InitialCreate`), на старте `Migrate`.
|
||||
- **Redis**: тикеты и записи очереди (JSON + sorted set по режимам, TTL);
|
||||
очередь поднимается при старте. Ключи с префиксом `vanguard:`.
|
||||
- **Конфигурация**: `Storage:Provider` = `None` / `Memory` / `Postgres`,
|
||||
|
|
@ -462,7 +463,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
|
|||
Критерий готовности серверного блока выполнен: два клиента играют полный
|
||||
матч по сети; kill клиента восстанавливается через новый тикет и
|
||||
`resumeFromSeq`; рестарт сервера восстанавливает матчи из PostgreSQL.
|
||||
Остались Orleans, миграции и привязка CI-runner.
|
||||
Остались Orleans и привязка CI-runner.
|
||||
|
||||
### 7.2. Мета-API
|
||||
|
||||
|
|
@ -519,7 +520,7 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
|
|||
`GameConnection`, логин, коллекция, декбилдер, магазин, очередь,
|
||||
история и реплей. Сервер и мета-API готовы: `docs/protocol.md`,
|
||||
`docs/server-api.md`, `Vanguard.Shared`.
|
||||
4. Orleans, миграции и CI-runner — отдельные серверные долги, не блокируют клиент.
|
||||
4. Orleans и CI-runner — отдельные серверные долги, не блокируют клиент.
|
||||
5. После каждого блока: тесты, `validate`, `translations check`, коммит, push.
|
||||
6. Не ломать `Vanguard.Rules`: сервер должен зависеть от него, а не наоборот.
|
||||
|
||||
|
|
@ -544,8 +545,8 @@ Solution — **`server/Vanguard.slnx`**, не `.sln`.
|
|||
- **Сервер: хранилище.** По умолчанию `Storage:Provider=None`: матчи живут
|
||||
в памяти (`NullMatchStore`), мета-API — в EF InMemory. Для рестарта нужен
|
||||
`Postgres` + `Storage:ConnectionString`; `Memory` — EF InMemory для всего.
|
||||
Схема создаётся `EnsureCreated` для одного общего `MatchDbContext`;
|
||||
миграций пока нет, при изменении схемы тестовую БД нужно пересоздать.
|
||||
Схема — EF-миграции (`Migrations/InitialCreate`), на старте `MigrateAsync`;
|
||||
для InMemory — `EnsureCreated`. Миграции применяются автоматически.
|
||||
- **Сервер: снапшот матча.** Сериализуется не состояние движка, а seed, колоды,
|
||||
игроки, часы и журнал интентов. При восстановлении команды проигрываются
|
||||
заново; `EventSeq` обязан совпасть, иначе матч пропускается с ошибкой.
|
||||
|
|
|
|||
|
|
@ -409,8 +409,10 @@ GET /api/replays/{matchId} → реплей
|
|||
|
||||
Восстановление матча: при старте `MatchRecoveryService` поднимает
|
||||
незавершённые матчи и проигрывает принятые команды на детерминированном
|
||||
движке; `eventSeq` сверяется. Схема создаётся `EnsureCreated`; миграций
|
||||
пока нет, при изменении схемы тестовую БД нужно пересоздать.
|
||||
движке; `eventSeq` сверяется. Схема — EF-миграции
|
||||
(`server/src/Vanguard.Server/Migrations`); на старте PostgreSQL выполняется
|
||||
`MigrateAsync`, для InMemory — `EnsureCreated`. Новая миграция:
|
||||
`dotnet ef migrations add <Name> ...`.
|
||||
|
||||
Redis: тикеты (`GETDEL`, TTL 60 с) и очередь (JSON + sorted set).
|
||||
|
||||
|
|
@ -421,7 +423,7 @@ Redis: тикеты (`GETDEL`, TTL 60 с) и очередь (JSON + sorted set).
|
|||
- `Storage:Provider=None` — всё в памяти, рестарт теряет данные.
|
||||
История матча пишется, но реплей при `None` недоступен: строки матча нет.
|
||||
Для истории и реплеев нужен `Memory` или `Postgres`.
|
||||
- `EnsureCreated`, а не миграции: схема меняется вместе с пересозданием БД.
|
||||
- `EnsureCreated` используется только для InMemory; PostgreSQL — миграции.
|
||||
- Один процесс: Orleans-хостинг ещё впереди; `IMatchGrain` уже отделён.
|
||||
- Нет магазина косметики, квестов, баттлпасса, Draft, друзей и чата.
|
||||
- Rate limit: 60/мин на auth, 300/мин на остальные мутирующие запросы.
|
||||
|
|
|
|||
463
server/src/Vanguard.Server/Migrations/20261021104418_InitialCreate.Designer.cs
generated
Normal file
463
server/src/Vanguard.Server/Migrations/20261021104418_InitialCreate.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Vanguard.Server.Storage;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Vanguard.Server.Migrations
|
||||
{
|
||||
[DbContext(typeof(MatchDbContext))]
|
||||
[Migration("20261021104418_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.BoosterOpeningEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("CardsJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("IdempotencyKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("Payment")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "IdempotencyKey")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("booster_openings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.CollectionEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("PrintingId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("UserId", "PrintingId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("collection", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.DeckEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("CardsJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Format")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("ShareCode")
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ShareCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("UserId", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("decks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.MatchHistoryEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("FinishReason")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<int>("Gold")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("MatchId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<int>("MmrAfter")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("MmrBefore")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Mode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("OpponentName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Result")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Xp")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "CreatedAt");
|
||||
|
||||
b.HasIndex("UserId", "MatchId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("match_history", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.RefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ReplacedByHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("RevokedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("refresh_tokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.TransactionEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Amount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Currency")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<string>("IdempotencyKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("MetadataJson")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "Currency");
|
||||
|
||||
b.HasIndex("UserId", "IdempotencyKey")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("transactions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.TrialDeckOwnershipEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DeckId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<DateTimeOffset>("AcquiredAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("UserId", "DeckId");
|
||||
|
||||
b.ToTable("trial_deck_ownership", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.UserEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("CasualMmr")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastLoginAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Matches")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<bool>("OnboardingCompleted")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<int>("RankedMmr")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("SelectedTrialDeckId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<int>("Wins")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Xp")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Storage.MatchEntity", b =>
|
||||
{
|
||||
b.Property<string>("MatchId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<DateTimeOffset?>("AbsentSince0")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset?>("AbsentSince1")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("Bank0Ms")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Bank1Ms")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Deck0Json")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Deck1Json")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("EventSeq")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("FinishReason")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("FinishedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("LastClockSent")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Mode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("Player0Id")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Player1Id")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Seed")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<bool>("Started")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("TurnDeadline")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Winner")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("MatchId");
|
||||
|
||||
b.HasIndex("IsFinished", "UpdatedAt");
|
||||
|
||||
b.ToTable("matches", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Storage.MatchIntentEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<bool>("Accepted")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Code")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Command")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("EventSeq")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("IntentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("MatchId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Seat")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MatchId");
|
||||
|
||||
b.ToTable("match_intents", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Vanguard.Server.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "booster_openings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
IdempotencyKey = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
Payment = table.Column<string>(type: "character varying(16)", maxLength: 16, nullable: false),
|
||||
CardsJson = table.Column<string>(type: "text", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_booster_openings", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "collection",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
PrintingId = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
Count = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_collection", x => new { x.UserId, x.PrintingId });
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "decks",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
Format = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
CardsJson = table.Column<string>(type: "text", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
ShareCode = table.Column<string>(type: "character varying(16)", maxLength: 16, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_decks", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "match_history",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
MatchId = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
Mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
OpponentName = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
Result = table.Column<string>(type: "character varying(16)", maxLength: 16, nullable: false),
|
||||
Gold = table.Column<int>(type: "integer", nullable: false),
|
||||
Xp = table.Column<int>(type: "integer", nullable: false),
|
||||
MmrBefore = table.Column<int>(type: "integer", nullable: false),
|
||||
MmrAfter = table.Column<int>(type: "integer", nullable: false),
|
||||
FinishReason = table.Column<string>(type: "character varying(16)", maxLength: 16, nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_match_history", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "match_intents",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
MatchId = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
IntentId = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
Seat = table.Column<int>(type: "integer", nullable: false),
|
||||
Command = table.Column<string>(type: "text", nullable: false),
|
||||
Accepted = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Code = table.Column<int>(type: "integer", nullable: false),
|
||||
Reason = table.Column<string>(type: "text", nullable: true),
|
||||
EventSeq = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_match_intents", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "matches",
|
||||
columns: table => new
|
||||
{
|
||||
MatchId = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
Mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
Player0Id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
Player1Id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
Deck0Json = table.Column<string>(type: "text", nullable: false),
|
||||
Deck1Json = table.Column<string>(type: "text", nullable: false),
|
||||
Seed = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
Started = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Bank0Ms = table.Column<long>(type: "bigint", nullable: false),
|
||||
Bank1Ms = table.Column<long>(type: "bigint", nullable: false),
|
||||
TurnDeadline = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
AbsentSince0 = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
AbsentSince1 = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
LastClockSent = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
IsFinished = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Winner = table.Column<int>(type: "integer", nullable: false),
|
||||
FinishedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
FinishReason = table.Column<string>(type: "text", nullable: false),
|
||||
EventSeq = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_matches", x => x.MatchId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "refresh_tokens",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TokenHash = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
ExpiresAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
RevokedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
ReplacedByHash = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_refresh_tokens", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "transactions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Currency = table.Column<string>(type: "character varying(16)", maxLength: 16, nullable: false),
|
||||
Amount = table.Column<int>(type: "integer", nullable: false),
|
||||
Reason = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
IdempotencyKey = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
MetadataJson = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_transactions", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "trial_deck_ownership",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
DeckId = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
AcquiredAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_trial_deck_ownership", x => new { x.UserId, x.DeckId });
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UserName = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
NormalizedUserName = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
LastLoginAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
OnboardingCompleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||
SelectedTrialDeckId = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
RankedMmr = table.Column<int>(type: "integer", nullable: false),
|
||||
CasualMmr = table.Column<int>(type: "integer", nullable: false),
|
||||
Matches = table.Column<int>(type: "integer", nullable: false),
|
||||
Wins = table.Column<int>(type: "integer", nullable: false),
|
||||
Xp = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_users", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_booster_openings_UserId_IdempotencyKey",
|
||||
table: "booster_openings",
|
||||
columns: new[] { "UserId", "IdempotencyKey" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_collection_UserId",
|
||||
table: "collection",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_decks_ShareCode",
|
||||
table: "decks",
|
||||
column: "ShareCode",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_decks_UserId",
|
||||
table: "decks",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_decks_UserId_Name",
|
||||
table: "decks",
|
||||
columns: new[] { "UserId", "Name" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_match_history_UserId_CreatedAt",
|
||||
table: "match_history",
|
||||
columns: new[] { "UserId", "CreatedAt" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_match_history_UserId_MatchId",
|
||||
table: "match_history",
|
||||
columns: new[] { "UserId", "MatchId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_match_intents_MatchId",
|
||||
table: "match_intents",
|
||||
column: "MatchId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_matches_IsFinished_UpdatedAt",
|
||||
table: "matches",
|
||||
columns: new[] { "IsFinished", "UpdatedAt" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_refresh_tokens_TokenHash",
|
||||
table: "refresh_tokens",
|
||||
column: "TokenHash",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_refresh_tokens_UserId",
|
||||
table: "refresh_tokens",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_transactions_UserId_Currency",
|
||||
table: "transactions",
|
||||
columns: new[] { "UserId", "Currency" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_transactions_UserId_IdempotencyKey",
|
||||
table: "transactions",
|
||||
columns: new[] { "UserId", "IdempotencyKey" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_users_NormalizedUserName",
|
||||
table: "users",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "booster_openings");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "collection");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "decks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "match_history");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "match_intents");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "matches");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "refresh_tokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "transactions");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "trial_deck_ownership");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,460 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Vanguard.Server.Storage;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Vanguard.Server.Migrations
|
||||
{
|
||||
[DbContext(typeof(MatchDbContext))]
|
||||
partial class MatchDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.BoosterOpeningEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("CardsJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("IdempotencyKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("Payment")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "IdempotencyKey")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("booster_openings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.CollectionEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("PrintingId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("UserId", "PrintingId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("collection", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.DeckEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("CardsJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Format")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("ShareCode")
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ShareCode")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("UserId", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("decks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.MatchHistoryEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("FinishReason")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<int>("Gold")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("MatchId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<int>("MmrAfter")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("MmrBefore")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Mode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("OpponentName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Result")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Xp")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "CreatedAt");
|
||||
|
||||
b.HasIndex("UserId", "MatchId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("match_history", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.RefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ReplacedByHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("RevokedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("refresh_tokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.TransactionEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Amount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Currency")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<string>("IdempotencyKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("MetadataJson")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId", "Currency");
|
||||
|
||||
b.HasIndex("UserId", "IdempotencyKey")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("transactions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.TrialDeckOwnershipEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DeckId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<DateTimeOffset>("AcquiredAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("UserId", "DeckId");
|
||||
|
||||
b.ToTable("trial_deck_ownership", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Meta.UserEntity", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("CasualMmr")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastLoginAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Matches")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<bool>("OnboardingCompleted")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<int>("RankedMmr")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("SelectedTrialDeckId")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<int>("Wins")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Xp")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Storage.MatchEntity", b =>
|
||||
{
|
||||
b.Property<string>("MatchId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<DateTimeOffset?>("AbsentSince0")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset?>("AbsentSince1")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("Bank0Ms")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Bank1Ms")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Deck0Json")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Deck1Json")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("EventSeq")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("FinishReason")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("FinishedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("LastClockSent")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Mode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<string>("Player0Id")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Player1Id")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Seed")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<bool>("Started")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("TurnDeadline")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Winner")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("MatchId");
|
||||
|
||||
b.HasIndex("IsFinished", "UpdatedAt");
|
||||
|
||||
b.ToTable("matches", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Vanguard.Server.Storage.MatchIntentEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<bool>("Accepted")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Code")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Command")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("EventSeq")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("IntentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("MatchId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Seat")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MatchId");
|
||||
|
||||
b.ToTable("match_intents", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -199,7 +199,10 @@ foreach (var deck in app.Services.GetRequiredService<TrialDeckCatalog>().Decks)
|
|||
var dbFactory = app.Services.GetRequiredService<IDbContextFactory<MatchDbContext>>();
|
||||
await using (var db = await dbFactory.CreateDbContextAsync())
|
||||
{
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
if (db.Database.IsRelational())
|
||||
await db.Database.MigrateAsync();
|
||||
else
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
}
|
||||
|
||||
// незавершённые матчи поднимаем до того, как сервер начнёт принимать подключения
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace Vanguard.Server.Storage;
|
||||
|
||||
/// <summary>
|
||||
/// Контекст для `dotnet ef`. EF не поднимает всё приложение: берёт фабрику
|
||||
/// и генерирует миграцию по модели. Строка подключения нужна только
|
||||
/// для выбора провайдера.
|
||||
/// </summary>
|
||||
public sealed class MatchDbContextDesignFactory : IDesignTimeDbContextFactory<MatchDbContext>
|
||||
{
|
||||
public MatchDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var connection = Environment.GetEnvironmentVariable("VANGUARD_DESIGN_CONNECTION")
|
||||
?? "Host=localhost;Database=vanguard;Username=vanguard;Password=vanguard";
|
||||
|
||||
var options = new DbContextOptionsBuilder<MatchDbContext>()
|
||||
.UseNpgsql(connection)
|
||||
.Options;
|
||||
|
||||
return new MatchDbContext(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,10 @@
|
|||
<PackageReference Include="BCrypt.Net-Next" Version="4.2.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.13" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.13.17" />
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class ExternalStorageTests
|
|||
await using (var db = new MatchDbContext(options))
|
||||
{
|
||||
await db.Database.EnsureDeletedAsync();
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
|
||||
var store = new EfMatchStore(new TestDbContextFactory(options), time);
|
||||
|
|
|
|||
Loading…
Reference in New Issue