mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
signup codes: admin-issued invite codes + site /join redemption
Port the December signup-code feature to the 2020.03 branch. While account creation is disabled, the only way in is now an admin-issued single-use signup code, redeemed on the public site's /join page. Because the 2020.03 watch login is device-id-bound with no code field, a new device refused at the signups-disabled gate (PlatformLogin / account create) is stashed in a PendingDevices table keyed by the real client IP (CF-Connecting-IP aware). The /join page shows the player the device their own client just reported; they enter code + username and the account is minted bound to that device, so the next launch logs straight in. No watch changes. - SignupCodeEntity + PendingDeviceEntity; tables created via idempotent CREATE TABLE IF NOT EXISTS at boot (Program.cs Postgres patch block + SQLite path) since they post-date the migration chain. - SignupCodeService: generate / list / revoke / redeem + pending-device capture (single-use, expiry, revoke validation). - Admin: GET/POST signup-codes + revoke endpoints and a Signup codes admin-ui page (generate with descriptor/expiry, status table, revoke). - Public: anonymous /api/site/v1/join pending-devices + redeem, and a /join site page (+ header link). - README documents the flow.
This commit is contained in:
@@ -138,6 +138,7 @@ builder.Services.AddScoped<GameSessionService>();
|
||||
builder.Services.AddScoped<PrivateInstanceService>();
|
||||
builder.Services.AddScoped<CommunityBoardService>();
|
||||
builder.Services.AddScoped<ServerSettingsService>();
|
||||
builder.Services.AddScoped<SignupCodeService>();
|
||||
// Singletons — these own connectionless state (Redis-backed or
|
||||
// process-local) and don't need a per-request scope.
|
||||
builder.Services.AddSingleton<NotificationService>();
|
||||
@@ -494,6 +495,35 @@ using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
db.Database.EnsureCreated();
|
||||
ApplySqliteCompatibilityPatches(db);
|
||||
// New tables that post-date this DB's EnsureCreated snapshot —
|
||||
// EnsureCreated never revisits an existing file, so create them
|
||||
// idempotently (matches the Postgres patch block below).
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"CREATE TABLE IF NOT EXISTS ""SignupCodes"" (
|
||||
""Id"" INTEGER NOT NULL CONSTRAINT ""PK_SignupCodes"" PRIMARY KEY AUTOINCREMENT,
|
||||
""Code"" TEXT NOT NULL,
|
||||
""Descriptor"" TEXT NOT NULL DEFAULT '',
|
||||
""CreatedByPlayerId"" INTEGER NOT NULL DEFAULT 0,
|
||||
""CreatedAt"" TEXT NOT NULL,
|
||||
""ExpiresAt"" TEXT NULL,
|
||||
""RedeemedByPlayerId"" INTEGER NULL,
|
||||
""RedeemedAt"" TEXT NULL,
|
||||
""Revoked"" INTEGER NOT NULL DEFAULT 0
|
||||
);");
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_SignupCodes_Code"" ON ""SignupCodes"" (""Code"");");
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"CREATE TABLE IF NOT EXISTS ""PendingDevices"" (
|
||||
""Id"" INTEGER NOT NULL CONSTRAINT ""PK_PendingDevices"" PRIMARY KEY AUTOINCREMENT,
|
||||
""DeviceId"" TEXT NOT NULL,
|
||||
""Platform"" INTEGER NOT NULL DEFAULT 0,
|
||||
""PlatformId"" TEXT NOT NULL DEFAULT '',
|
||||
""LastIp"" TEXT NULL,
|
||||
""FirstSeenAt"" TEXT NOT NULL,
|
||||
""LastSeenAt"" TEXT NOT NULL
|
||||
);");
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_PendingDevices_DeviceId"" ON ""PendingDevices"" (""DeviceId"");");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -793,6 +823,36 @@ using (var scope = app.Services.CreateScope())
|
||||
""UpdatedAt"" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);");
|
||||
|
||||
// 2026-05-31 — Signup codes + pending-device capture for the
|
||||
// admin-issued invite flow (site /join). New tables, so
|
||||
// EnsureCreated-skipped on existing DBs.
|
||||
await RunPatchAsync("SignupCodes table",
|
||||
@"CREATE TABLE IF NOT EXISTS ""SignupCodes"" (
|
||||
""Id"" bigserial PRIMARY KEY,
|
||||
""Code"" text NOT NULL,
|
||||
""Descriptor"" text NOT NULL DEFAULT '',
|
||||
""CreatedByPlayerId"" bigint NOT NULL DEFAULT 0,
|
||||
""CreatedAt"" timestamp with time zone NOT NULL DEFAULT now(),
|
||||
""ExpiresAt"" timestamp with time zone NULL,
|
||||
""RedeemedByPlayerId"" bigint NULL,
|
||||
""RedeemedAt"" timestamp with time zone NULL,
|
||||
""Revoked"" boolean NOT NULL DEFAULT false
|
||||
);");
|
||||
await RunPatchAsync("SignupCodes Code unique index",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_SignupCodes_Code"" ON ""SignupCodes"" (""Code"");");
|
||||
await RunPatchAsync("PendingDevices table",
|
||||
@"CREATE TABLE IF NOT EXISTS ""PendingDevices"" (
|
||||
""Id"" bigserial PRIMARY KEY,
|
||||
""DeviceId"" text NOT NULL,
|
||||
""Platform"" integer NOT NULL DEFAULT 0,
|
||||
""PlatformId"" text NOT NULL DEFAULT '',
|
||||
""LastIp"" text NULL,
|
||||
""FirstSeenAt"" timestamp with time zone NOT NULL DEFAULT now(),
|
||||
""LastSeenAt"" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);");
|
||||
await RunPatchAsync("PendingDevices DeviceId unique index",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_PendingDevices_DeviceId"" ON ""PendingDevices"" (""DeviceId"");");
|
||||
|
||||
await conn.CloseAsync();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user