mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-26 23:48:45 -07:00
feat: Add new API controllers for progression events, room consumables, currencies, earnings distributions, screen share reports, subscription seasons, and UGC purchasables
- Implemented ProgressionEventsController to handle progression event retrieval and active events. - Created RoomConsumablesController for listing and purchasing room consumables. - Developed RoomCurrenciesController for managing room currencies, including creation, updates, and balance retrieval. - Added RoomEarningsDistributionsController to fetch earnings distribution data for rooms. - Introduced ScreenShareReportsController for reporting screen share incidents. - Established SubscriptionSeasonsController to provide current subscription season details and rewards. - Launched UgcPurchasablesController for managing user-generated content purchasables, including bulk operations. - Added new data entities for customization and room economy, including custom avatar items, room currencies, and UGC purchasables.
This commit is contained in:
@@ -104,6 +104,8 @@ public static class DatabaseBootstrap
|
||||
await EnsureInventorySchemaAsync(db);
|
||||
await EnsureLeaderboardSchemaAsync(db);
|
||||
await EnsureRoomColumnsAsync(db);
|
||||
await EnsureInventionColumnsAsync(db);
|
||||
await EnsureMarch2023TablesAsync(db);
|
||||
|
||||
// Coach system account at Player.Id=1. The RR-Original room seeder
|
||||
// below assigns CreatorPlayerId=1 to every canonical room
|
||||
@@ -251,6 +253,12 @@ public static class DatabaseBootstrap
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN IF NOT EXISTS ""MaxCapacity"" integer NOT NULL DEFAULT 8;");
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN IF NOT EXISTS ""IsStudioRoom"" boolean NOT NULL DEFAULT false;");
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN IF NOT EXISTS ""IsRoomLinkedToRecRoomStudio"" boolean NOT NULL DEFAULT false;");
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN IF NOT EXISTS ""StudioSessionId"" character varying(128) NOT NULL DEFAULT '';");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -262,6 +270,45 @@ public static class DatabaseBootstrap
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN ""MaxCapacity"" INTEGER NOT NULL DEFAULT 8;");
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN ""IsStudioRoom"" INTEGER NOT NULL DEFAULT 0;");
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN ""IsRoomLinkedToRecRoomStudio"" INTEGER NOT NULL DEFAULT 0;");
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Rooms"" ADD COLUMN ""StudioSessionId"" TEXT NOT NULL DEFAULT '';");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static async Task EnsureInventionColumnsAsync(DorkNetDbContext db)
|
||||
{
|
||||
var provider = db.Database.ProviderName ?? string.Empty;
|
||||
if (provider.Contains("Npgsql", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Inventions"" ADD COLUMN IF NOT EXISTS ""Price"" integer NOT NULL DEFAULT 0;");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
@"ALTER TABLE ""Inventions"" ADD COLUMN ""Price"" INTEGER NOT NULL DEFAULT 0;");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static async Task EnsureServerSettingsColumnsAsync(DorkNetDbContext db)
|
||||
@@ -464,6 +511,280 @@ public static class DatabaseBootstrap
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_LeaderboardStats_RoomId_StatChannel_Value"" ON ""LeaderboardStats"" (""RoomId"", ""StatChannel"", ""Value"");");
|
||||
}
|
||||
|
||||
private static async Task EnsureMarch2023TablesAsync(DorkNetDbContext db)
|
||||
{
|
||||
var provider = db.Database.ProviderName ?? string.Empty;
|
||||
if (provider.Contains("Npgsql", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var statements = new[]
|
||||
{
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "CustomAvatarItems" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PublicId" uuid NOT NULL,
|
||||
"CreatorPlayerId" bigint NOT NULL,
|
||||
"Name" character varying(128) NOT NULL,
|
||||
"Description" character varying(1024) NOT NULL,
|
||||
"Price" integer NOT NULL DEFAULT 100,
|
||||
"ItemType" integer NOT NULL DEFAULT 0,
|
||||
"BaseAvatarItemId" integer NOT NULL DEFAULT 0,
|
||||
"Color" character varying(64) NOT NULL DEFAULT '',
|
||||
"ImageName" character varying(256) NOT NULL DEFAULT '',
|
||||
"AssetName" character varying(256) NOT NULL DEFAULT '',
|
||||
"IsPublic" boolean NOT NULL DEFAULT false,
|
||||
"IsFeatured" boolean NOT NULL DEFAULT false,
|
||||
"CheerCount" bigint NOT NULL DEFAULT 0,
|
||||
"PurchaseCount" bigint NOT NULL DEFAULT 0,
|
||||
"CreatedAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||
"UpdatedAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_PublicId"" ON ""CustomAvatarItems"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_CreatorPlayerId"" ON ""CustomAvatarItems"" (""CreatorPlayerId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_IsPublic"" ON ""CustomAvatarItems"" (""IsPublic"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_IsFeatured"" ON ""CustomAvatarItems"" (""IsFeatured"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "CustomAvatarItemOwnership" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PlayerId" bigint NOT NULL,
|
||||
"CustomAvatarItemId" bigint NOT NULL,
|
||||
"AcquiredAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_CustomAvatarItemOwnership_PlayerId_CustomAvatarItemId"" ON ""CustomAvatarItemOwnership"" (""PlayerId"", ""CustomAvatarItemId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItemOwnership_CustomAvatarItemId"" ON ""CustomAvatarItemOwnership"" (""CustomAvatarItemId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "ItemWishlists" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PlayerId" bigint NOT NULL,
|
||||
"ItemKey" character varying(128) NOT NULL,
|
||||
"ItemType" integer NOT NULL DEFAULT 0,
|
||||
"CreatedAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_ItemWishlists_PlayerId_ItemKey_ItemType"" ON ""ItemWishlists"" (""PlayerId"", ""ItemKey"", ""ItemType"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "Keepsakes" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PlayerId" bigint NOT NULL,
|
||||
"Category" character varying(64) NOT NULL DEFAULT 'general',
|
||||
"EventKey" character varying(128) NOT NULL DEFAULT '',
|
||||
"Title" character varying(128) NOT NULL DEFAULT '',
|
||||
"Description" character varying(1024) NOT NULL DEFAULT '',
|
||||
"ImageName" character varying(256) NOT NULL DEFAULT '',
|
||||
"EarnedAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_Keepsakes_PlayerId"" ON ""Keepsakes"" (""PlayerId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_Keepsakes_PlayerId_EventKey"" ON ""Keepsakes"" (""PlayerId"", ""EventKey"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "RoomCurrencies" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PublicId" uuid NOT NULL,
|
||||
"RoomId" bigint NOT NULL,
|
||||
"CreatorPlayerId" bigint NOT NULL,
|
||||
"Name" character varying(64) NOT NULL DEFAULT '',
|
||||
"Description" character varying(512) NOT NULL DEFAULT '',
|
||||
"ImageName" character varying(256) NOT NULL DEFAULT '',
|
||||
"DailyLimit" integer NOT NULL DEFAULT 0,
|
||||
"IsDeleted" boolean NOT NULL DEFAULT false,
|
||||
"CreatedAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||
"UpdatedAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_RoomCurrencies_PublicId"" ON ""RoomCurrencies"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencies_RoomId"" ON ""RoomCurrencies"" (""RoomId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencies_CreatorPlayerId"" ON ""RoomCurrencies"" (""CreatorPlayerId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "RoomCurrencyBalances" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PlayerId" bigint NOT NULL,
|
||||
"RoomCurrencyId" bigint NOT NULL,
|
||||
"Balance" bigint NOT NULL DEFAULT 0,
|
||||
"UpdatedAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_RoomCurrencyBalances_PlayerId_RoomCurrencyId"" ON ""RoomCurrencyBalances"" (""PlayerId"", ""RoomCurrencyId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencyBalances_RoomCurrencyId"" ON ""RoomCurrencyBalances"" (""RoomCurrencyId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "RoomCurrencyPurchaseOffers" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PublicId" uuid NOT NULL,
|
||||
"RoomCurrencyId" bigint NOT NULL,
|
||||
"Name" character varying(64) NOT NULL DEFAULT '',
|
||||
"Amount" integer NOT NULL DEFAULT 0,
|
||||
"Price" integer NOT NULL DEFAULT 0,
|
||||
"CurrencyType" integer NOT NULL DEFAULT 2,
|
||||
"IsDeleted" boolean NOT NULL DEFAULT false,
|
||||
"CreatedAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||
"UpdatedAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_RoomCurrencyPurchaseOffers_PublicId"" ON ""RoomCurrencyPurchaseOffers"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencyPurchaseOffers_RoomCurrencyId"" ON ""RoomCurrencyPurchaseOffers"" (""RoomCurrencyId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "UgcPurchasables" (
|
||||
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"PublicId" uuid NOT NULL,
|
||||
"RoomId" bigint NOT NULL,
|
||||
"CreatorPlayerId" bigint NOT NULL,
|
||||
"Name" character varying(128) NOT NULL DEFAULT '',
|
||||
"Description" character varying(1024) NOT NULL DEFAULT '',
|
||||
"ImageName" character varying(256) NOT NULL DEFAULT '',
|
||||
"Price" integer NOT NULL DEFAULT 0,
|
||||
"CurrencyType" integer NOT NULL DEFAULT 2,
|
||||
"ItemType" integer NOT NULL DEFAULT 0,
|
||||
"IsFeatured" boolean NOT NULL DEFAULT false,
|
||||
"SortOrder" integer NOT NULL DEFAULT 0,
|
||||
"IsDeleted" boolean NOT NULL DEFAULT false,
|
||||
"CreatedAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||
"UpdatedAt" timestamp with time zone NOT NULL DEFAULT now()
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_UgcPurchasables_PublicId"" ON ""UgcPurchasables"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_UgcPurchasables_RoomId"" ON ""UgcPurchasables"" (""RoomId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_UgcPurchasables_CreatorPlayerId"" ON ""UgcPurchasables"" (""CreatorPlayerId"");",
|
||||
};
|
||||
|
||||
foreach (var sql in statements)
|
||||
await db.Database.ExecuteSqlRawAsync(sql);
|
||||
return;
|
||||
}
|
||||
|
||||
var sqliteStatements = new[]
|
||||
{
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "CustomAvatarItems" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_CustomAvatarItems" PRIMARY KEY AUTOINCREMENT,
|
||||
"PublicId" TEXT NOT NULL,
|
||||
"CreatorPlayerId" INTEGER NOT NULL,
|
||||
"Name" TEXT NOT NULL,
|
||||
"Description" TEXT NOT NULL,
|
||||
"Price" INTEGER NOT NULL DEFAULT 100,
|
||||
"ItemType" INTEGER NOT NULL DEFAULT 0,
|
||||
"BaseAvatarItemId" INTEGER NOT NULL DEFAULT 0,
|
||||
"Color" TEXT NOT NULL DEFAULT '',
|
||||
"ImageName" TEXT NOT NULL DEFAULT '',
|
||||
"AssetName" TEXT NOT NULL DEFAULT '',
|
||||
"IsPublic" INTEGER NOT NULL DEFAULT 0,
|
||||
"IsFeatured" INTEGER NOT NULL DEFAULT 0,
|
||||
"CheerCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"PurchaseCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_PublicId"" ON ""CustomAvatarItems"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_CreatorPlayerId"" ON ""CustomAvatarItems"" (""CreatorPlayerId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_IsPublic"" ON ""CustomAvatarItems"" (""IsPublic"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItems_IsFeatured"" ON ""CustomAvatarItems"" (""IsFeatured"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "CustomAvatarItemOwnership" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_CustomAvatarItemOwnership" PRIMARY KEY AUTOINCREMENT,
|
||||
"PlayerId" INTEGER NOT NULL,
|
||||
"CustomAvatarItemId" INTEGER NOT NULL,
|
||||
"AcquiredAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_CustomAvatarItemOwnership_PlayerId_CustomAvatarItemId"" ON ""CustomAvatarItemOwnership"" (""PlayerId"", ""CustomAvatarItemId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_CustomAvatarItemOwnership_CustomAvatarItemId"" ON ""CustomAvatarItemOwnership"" (""CustomAvatarItemId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "ItemWishlists" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_ItemWishlists" PRIMARY KEY AUTOINCREMENT,
|
||||
"PlayerId" INTEGER NOT NULL,
|
||||
"ItemKey" TEXT NOT NULL,
|
||||
"ItemType" INTEGER NOT NULL DEFAULT 0,
|
||||
"CreatedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_ItemWishlists_PlayerId_ItemKey_ItemType"" ON ""ItemWishlists"" (""PlayerId"", ""ItemKey"", ""ItemType"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "Keepsakes" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_Keepsakes" PRIMARY KEY AUTOINCREMENT,
|
||||
"PlayerId" INTEGER NOT NULL,
|
||||
"Category" TEXT NOT NULL DEFAULT 'general',
|
||||
"EventKey" TEXT NOT NULL DEFAULT '',
|
||||
"Title" TEXT NOT NULL DEFAULT '',
|
||||
"Description" TEXT NOT NULL DEFAULT '',
|
||||
"ImageName" TEXT NOT NULL DEFAULT '',
|
||||
"EarnedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_Keepsakes_PlayerId"" ON ""Keepsakes"" (""PlayerId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_Keepsakes_PlayerId_EventKey"" ON ""Keepsakes"" (""PlayerId"", ""EventKey"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "RoomCurrencies" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_RoomCurrencies" PRIMARY KEY AUTOINCREMENT,
|
||||
"PublicId" TEXT NOT NULL,
|
||||
"RoomId" INTEGER NOT NULL,
|
||||
"CreatorPlayerId" INTEGER NOT NULL,
|
||||
"Name" TEXT NOT NULL DEFAULT '',
|
||||
"Description" TEXT NOT NULL DEFAULT '',
|
||||
"ImageName" TEXT NOT NULL DEFAULT '',
|
||||
"DailyLimit" INTEGER NOT NULL DEFAULT 0,
|
||||
"IsDeleted" INTEGER NOT NULL DEFAULT 0,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_RoomCurrencies_PublicId"" ON ""RoomCurrencies"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencies_RoomId"" ON ""RoomCurrencies"" (""RoomId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencies_CreatorPlayerId"" ON ""RoomCurrencies"" (""CreatorPlayerId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "RoomCurrencyBalances" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_RoomCurrencyBalances" PRIMARY KEY AUTOINCREMENT,
|
||||
"PlayerId" INTEGER NOT NULL,
|
||||
"RoomCurrencyId" INTEGER NOT NULL,
|
||||
"Balance" INTEGER NOT NULL DEFAULT 0,
|
||||
"UpdatedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_RoomCurrencyBalances_PlayerId_RoomCurrencyId"" ON ""RoomCurrencyBalances"" (""PlayerId"", ""RoomCurrencyId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencyBalances_RoomCurrencyId"" ON ""RoomCurrencyBalances"" (""RoomCurrencyId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "RoomCurrencyPurchaseOffers" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_RoomCurrencyPurchaseOffers" PRIMARY KEY AUTOINCREMENT,
|
||||
"PublicId" TEXT NOT NULL,
|
||||
"RoomCurrencyId" INTEGER NOT NULL,
|
||||
"Name" TEXT NOT NULL DEFAULT '',
|
||||
"Amount" INTEGER NOT NULL DEFAULT 0,
|
||||
"Price" INTEGER NOT NULL DEFAULT 0,
|
||||
"CurrencyType" INTEGER NOT NULL DEFAULT 2,
|
||||
"IsDeleted" INTEGER NOT NULL DEFAULT 0,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_RoomCurrencyPurchaseOffers_PublicId"" ON ""RoomCurrencyPurchaseOffers"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_RoomCurrencyPurchaseOffers_RoomCurrencyId"" ON ""RoomCurrencyPurchaseOffers"" (""RoomCurrencyId"");",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "UgcPurchasables" (
|
||||
"Id" INTEGER NOT NULL CONSTRAINT "PK_UgcPurchasables" PRIMARY KEY AUTOINCREMENT,
|
||||
"PublicId" TEXT NOT NULL,
|
||||
"RoomId" INTEGER NOT NULL,
|
||||
"CreatorPlayerId" INTEGER NOT NULL,
|
||||
"Name" TEXT NOT NULL DEFAULT '',
|
||||
"Description" TEXT NOT NULL DEFAULT '',
|
||||
"ImageName" TEXT NOT NULL DEFAULT '',
|
||||
"Price" INTEGER NOT NULL DEFAULT 0,
|
||||
"CurrencyType" INTEGER NOT NULL DEFAULT 2,
|
||||
"ItemType" INTEGER NOT NULL DEFAULT 0,
|
||||
"IsFeatured" INTEGER NOT NULL DEFAULT 0,
|
||||
"SortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
"IsDeleted" INTEGER NOT NULL DEFAULT 0,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
@"CREATE UNIQUE INDEX IF NOT EXISTS ""IX_UgcPurchasables_PublicId"" ON ""UgcPurchasables"" (""PublicId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_UgcPurchasables_RoomId"" ON ""UgcPurchasables"" (""RoomId"");",
|
||||
@"CREATE INDEX IF NOT EXISTS ""IX_UgcPurchasables_CreatorPlayerId"" ON ""UgcPurchasables"" (""CreatorPlayerId"");",
|
||||
};
|
||||
|
||||
foreach (var sql in sqliteStatements)
|
||||
await db.Database.ExecuteSqlRawAsync(sql);
|
||||
}
|
||||
|
||||
/// <summary>SQLite migration-history reconciliation. Handles two
|
||||
/// cutover scenarios so legacy DBs boot cleanly without manual SQL:
|
||||
/// <list type="number">
|
||||
|
||||
@@ -128,14 +128,15 @@ public static class RequestTracingExtensions
|
||||
var trimmedRequest = safeRequest is null ? null
|
||||
: (safeRequest.Length > 200 ? safeRequest[..200] + "..." : safeRequest);
|
||||
var trimmedResponse = safeResponse.Length > 300 ? safeResponse[..300] + "..." : safeResponse;
|
||||
var authPresence = DescribeAuthPresence(ctx.Request);
|
||||
var level = thrown is not null || status >= 500 ? LogLevel.Error
|
||||
: status >= 400 ? LogLevel.Warning
|
||||
: LogLevel.Information;
|
||||
|
||||
reqLogger.Log(level, thrown,
|
||||
"[req] {Status} {Method} {Host}{Path}{Query} {ElapsedMs}ms contentType={ContentType} contentLength={ContentLength} req={ReqBody} resp={RespBody}",
|
||||
"[req] {Status} {Method} {Host}{Path}{Query} {ElapsedMs}ms auth={AuthPresence} contentType={ContentType} contentLength={ContentLength} req={ReqBody} resp={RespBody}",
|
||||
status, ctx.Request.Method, ctx.Request.Host, ctx.Request.Path,
|
||||
ctx.Request.QueryString.Value ?? "", sw.ElapsedMilliseconds,
|
||||
ctx.Request.QueryString.Value ?? "", sw.ElapsedMilliseconds, authPresence,
|
||||
ctx.Request.ContentType ?? "", ctx.Request.ContentLength,
|
||||
trimmedRequest ?? "", trimmedResponse);
|
||||
|
||||
@@ -170,6 +171,15 @@ public static class RequestTracingExtensions
|
||||
"password|passwd|pwd|passwordhash|token|accesstoken|refreshtoken|idtoken|" +
|
||||
"secret|clientsecret|apikey|api_key|authorization|deviceauth|sessionkey";
|
||||
|
||||
private static string DescribeAuthPresence(HttpRequest request)
|
||||
{
|
||||
var hasAuthHeader = request.Headers.ContainsKey("Authorization");
|
||||
var hasBearer = request.Headers.Authorization.ToString()
|
||||
.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase);
|
||||
var hasAccessCookie = request.Cookies.ContainsKey(AuthService.AccessCookieName);
|
||||
return $"header={hasAuthHeader};bearer={hasBearer};cookie={hasAccessCookie}";
|
||||
}
|
||||
|
||||
private static readonly Regex JsonSecretRegex = new(
|
||||
"(\"(?:" + SecretKeys + ")\"\\s*:\\s*)\"(?:[^\"\\\\]|\\\\.)*\"",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
@@ -107,14 +107,22 @@ public static class ServiceCollectionExtensions
|
||||
?? Environment.GetEnvironmentVariable("DORKNET_DOMAIN")
|
||||
?? "localhost";
|
||||
var scheme = builder.Configuration["Domain:Scheme"] ?? "https";
|
||||
var allowedHosts = new List<string> { apex, $"*.{apex}", "localhost", "127.0.0.1" };
|
||||
var bootstrapApex = builder.Configuration["Domain:BootstrapApex"];
|
||||
if (!string.IsNullOrWhiteSpace(bootstrapApex))
|
||||
{
|
||||
allowedHosts.Add(bootstrapApex);
|
||||
allowedHosts.Add($"*.{bootstrapApex}");
|
||||
}
|
||||
|
||||
builder.Services.AddSingleton(new DomainConfig(apex, scheme));
|
||||
builder.Services.Configure<Microsoft.AspNetCore.HostFiltering.HostFilteringOptions>(opt =>
|
||||
{
|
||||
opt.AllowedHosts = new[] { apex, $"*.{apex}", "localhost", "127.0.0.1" };
|
||||
opt.AllowedHosts = allowedHosts.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
|
||||
opt.AllowEmptyHosts = true;
|
||||
opt.IncludeFailureMessage = true;
|
||||
});
|
||||
Console.WriteLine($"[domain] apex={apex}, allowedHosts=[{apex}, *.{apex}, localhost]");
|
||||
Console.WriteLine($"[domain] apex={apex}, allowedHosts=[{string.Join(", ", allowedHosts)}]");
|
||||
}
|
||||
|
||||
// ── Services ──────────────────────────────────────────────────────────────
|
||||
@@ -174,17 +182,16 @@ public static class ServiceCollectionExtensions
|
||||
var pluginInstances = new IVersionPlugin[]
|
||||
{
|
||||
new Late2020VersionPlugin(),
|
||||
// Add March2020 / future generations here as their plugins land.
|
||||
};
|
||||
foreach (var p in pluginInstances) p.RegisterStrategies(builder.Services);
|
||||
foreach (var p in pluginInstances)
|
||||
builder.Services.AddSingleton<IVersionPlugin>(p);
|
||||
|
||||
var supported = (builder.Configuration.GetSection("DorkNet:SupportedVersions").Get<string[]>()
|
||||
?? new[] { "december_2020_12_18" })
|
||||
?? new[] { "march_2023_03_21" })
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var defaultKey = builder.Configuration["DorkNet:DefaultClientVersion"]
|
||||
?? "december_2020_12_18";
|
||||
?? "march_2023_03_21";
|
||||
builder.Services.AddSingleton(sp => new VersionRegistry(
|
||||
sp.GetServices<IVersionPlugin>(),
|
||||
supported,
|
||||
@@ -285,6 +292,12 @@ public static class ServiceCollectionExtensions
|
||||
var path = ctx.HttpContext.Request.Path;
|
||||
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hub"))
|
||||
ctx.Token = accessToken;
|
||||
if (string.IsNullOrEmpty(ctx.Token) &&
|
||||
ctx.Request.Cookies.TryGetValue(AuthService.AccessCookieName, out var cookieToken) &&
|
||||
!string.IsNullOrWhiteSpace(cookieToken))
|
||||
{
|
||||
ctx.Token = cookieToken;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user