feat: rearrange game benchmark wizard and improve overall
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
user,
|
||||
} from "@/lib/db/schema"
|
||||
import { eq, desc, sql, and, ilike, isNull, isNotNull, or } from "drizzle-orm"
|
||||
import { fuzzySearchTerm } from "@/lib/db/search"
|
||||
import {
|
||||
requireContributorOrAdmin,
|
||||
requireAdmin,
|
||||
@@ -47,7 +48,7 @@ export const adminPerformanceRoutes = new Elysia({ prefix: "/admin" })
|
||||
conditions.push(
|
||||
or(
|
||||
ilike(user.name, `%${searchTerm}%`),
|
||||
ilike(games.title, `%${searchTerm}%`),
|
||||
ilike(games.title, fuzzySearchTerm(searchTerm)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
or,
|
||||
} from "drizzle-orm"
|
||||
import { requireRole } from "@/lib/auth/guard"
|
||||
import { fuzzySearchTerm } from "@/lib/db/search"
|
||||
|
||||
/** Which columns are text-searchable via ilike */
|
||||
export type CrudSearchConfig = {
|
||||
@@ -94,10 +95,15 @@ export function createCrudRoutes<T extends AnyPgTable>(
|
||||
|
||||
// Search
|
||||
if (query.search && search) {
|
||||
const searchStr = query.search
|
||||
const searchConditions = search.fields
|
||||
.map((field) => {
|
||||
const col = columns[field]
|
||||
return col ? ilike(col, `%${query.search}%`) : null
|
||||
if (!col) return null
|
||||
const pattern = field === "title"
|
||||
? fuzzySearchTerm(searchStr)
|
||||
: `%${searchStr}%`
|
||||
return ilike(col, pattern)
|
||||
})
|
||||
.filter(Boolean) as SQL[]
|
||||
if (searchConditions.length > 0) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
hardware,
|
||||
} from "@/lib/db/schema"
|
||||
import { ilike, or, sql, eq, and, desc, asc, inArray, gte, lte } from "drizzle-orm"
|
||||
import { fuzzySearchTerm } from "@/lib/db/search"
|
||||
|
||||
const MAX_OFFSET = 10000
|
||||
const PAGE_SIZE = 24
|
||||
@@ -39,10 +40,11 @@ export const gamesListingRoutes = new Elysia({ prefix: "/games/listing" }).get(
|
||||
|
||||
// Search filter (title, developer, publisher)
|
||||
if (search) {
|
||||
const titleTerm = fuzzySearchTerm(search)
|
||||
const term = `%${search}%`
|
||||
conditions.push(
|
||||
or(
|
||||
ilike(games.title, term),
|
||||
ilike(games.title, titleTerm),
|
||||
ilike(games.developer, term),
|
||||
ilike(games.publisher, term),
|
||||
)!,
|
||||
|
||||
@@ -4,8 +4,9 @@ import {
|
||||
performanceEntries,
|
||||
gameVersions,
|
||||
hardware,
|
||||
gamePlatformSupport,
|
||||
} from "@/lib/db/schema"
|
||||
import { eq, sql } from "drizzle-orm"
|
||||
import { eq, and, sql } from "drizzle-orm"
|
||||
import { requireRole } from "@/lib/auth/guard"
|
||||
import { recalculatePlayability } from "./playability"
|
||||
|
||||
@@ -72,7 +73,7 @@ export const performanceSubmitRoutes = new Elysia({ prefix: "/performance" })
|
||||
|
||||
// Verify the game version exists
|
||||
const [version] = await db
|
||||
.select({ id: gameVersions.id })
|
||||
.select({ id: gameVersions.id, gameId: gameVersions.gameId })
|
||||
.from(gameVersions)
|
||||
.where(eq(gameVersions.id, body.versionId))
|
||||
.limit(1)
|
||||
@@ -120,18 +121,48 @@ export const performanceSubmitRoutes = new Elysia({ prefix: "/performance" })
|
||||
})
|
||||
.returning()
|
||||
|
||||
// Recalculate playability for this game (fire and forget)
|
||||
const [gameVersion] = await db
|
||||
.select({ gameId: gameVersions.gameId })
|
||||
.from(gameVersions)
|
||||
.where(eq(gameVersions.id, body.versionId))
|
||||
.limit(1)
|
||||
if (gameVersion) {
|
||||
recalculatePlayability(gameVersion.gameId).catch((err) =>
|
||||
console.error("Failed to recalculate playability:", err),
|
||||
// Update or create gamePlatformSupport with anti-cheat info
|
||||
const [existingSupport] = await db
|
||||
.select()
|
||||
.from(gamePlatformSupport)
|
||||
.where(
|
||||
and(
|
||||
eq(gamePlatformSupport.gameId, version.gameId),
|
||||
eq(gamePlatformSupport.hardwareSlug, body.hardwareSlug),
|
||||
),
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
if (existingSupport) {
|
||||
await db
|
||||
.update(gamePlatformSupport)
|
||||
.set({
|
||||
antiCheatRelevant: body.antiCheatRelevant ?? existingSupport.antiCheatRelevant,
|
||||
antiCheatName: body.antiCheatRelevant
|
||||
? (body.antiCheatName ?? existingSupport.antiCheatName)
|
||||
: null,
|
||||
antiCheatStatus: body.antiCheatStatus ?? existingSupport.antiCheatStatus,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(gamePlatformSupport.id, existingSupport.id))
|
||||
} else {
|
||||
await db.insert(gamePlatformSupport).values({
|
||||
gameId: version.gameId,
|
||||
hardwareSlug: body.hardwareSlug,
|
||||
isSupported: true,
|
||||
protonStatus: "unknown",
|
||||
antiCheatRelevant: body.antiCheatRelevant ?? false,
|
||||
antiCheatName: body.antiCheatRelevant ? body.antiCheatName ?? null : null,
|
||||
antiCheatStatus: body.antiCheatStatus ?? "unknown",
|
||||
playabilityStatus: "unknown",
|
||||
})
|
||||
}
|
||||
|
||||
// Recalculate playability for this game (fire and forget)
|
||||
recalculatePlayability(version.gameId).catch((err) =>
|
||||
console.error("Failed to recalculate playability:", err),
|
||||
)
|
||||
|
||||
set.status = 201
|
||||
return {
|
||||
id: entry.id,
|
||||
@@ -190,6 +221,16 @@ export const performanceSubmitRoutes = new Elysia({ prefix: "/performance" })
|
||||
]),
|
||||
),
|
||||
userNotes: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
antiCheatRelevant: t.Optional(t.Boolean()),
|
||||
antiCheatName: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
antiCheatStatus: t.Optional(
|
||||
t.Union([
|
||||
t.Literal("none"),
|
||||
t.Literal("supported"),
|
||||
t.Literal("unsupported"),
|
||||
t.Literal("unknown"),
|
||||
]),
|
||||
),
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
+87
-9
@@ -1,6 +1,6 @@
|
||||
import { Elysia, t } from "elysia"
|
||||
import { createCrudRoutes } from "./crud-builder"
|
||||
import { performanceEntries, games, gameVersions, hardware, user } from "@/lib/db/schema"
|
||||
import { performanceEntries, games, gameVersions, hardware, user, gamePlatformSupport } from "@/lib/db/schema"
|
||||
import { db } from "@/lib/db/index"
|
||||
import { eq, and, desc, sql } from "drizzle-orm"
|
||||
import { requireRole } from "@/lib/auth/guard"
|
||||
@@ -268,10 +268,10 @@ export const performanceVerifyRoutes = new Elysia({
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
if (body.fpsAvg !== undefined) updateData.fpsAvg = body.fpsAvg
|
||||
if (body.fpsOnePercentLow !== undefined) updateData.fpsOnePercentLow = body.fpsOnePercentLow
|
||||
if (body.fpsLow !== undefined) updateData.fpsLow = body.fpsLow
|
||||
if (body.fpsHigh !== undefined) updateData.fpsHigh = body.fpsHigh
|
||||
if (body.fpsAvg !== undefined) updateData.fpsAvg = body.fpsAvg ?? undefined
|
||||
if (body.fpsOnePercentLow !== undefined) updateData.fpsOnePercentLow = body.fpsOnePercentLow ?? undefined
|
||||
if (body.fpsLow !== undefined) updateData.fpsLow = body.fpsLow ?? undefined
|
||||
if (body.fpsHigh !== undefined) updateData.fpsHigh = body.fpsHigh ?? undefined
|
||||
if (body.protonVersion !== undefined)
|
||||
updateData.protonVersion = body.protonVersion
|
||||
if (body.osVersion !== undefined)
|
||||
@@ -295,15 +295,82 @@ export const performanceVerifyRoutes = new Elysia({
|
||||
.where(eq(performanceEntries.id, params.id))
|
||||
.returning()
|
||||
|
||||
// Update gamePlatformSupport anti-cheat info if provided
|
||||
if (
|
||||
body.antiCheatRelevant !== undefined ||
|
||||
body.antiCheatName !== undefined ||
|
||||
body.antiCheatStatus !== undefined
|
||||
) {
|
||||
// Need versionId to resolve gameId
|
||||
const [entryVersion] = await db
|
||||
.select({ versionId: performanceEntries.versionId })
|
||||
.from(performanceEntries)
|
||||
.where(eq(performanceEntries.id, params.id))
|
||||
.limit(1)
|
||||
|
||||
if (entryVersion) {
|
||||
const [gv] = await db
|
||||
.select({ gameId: gameVersions.gameId })
|
||||
.from(gameVersions)
|
||||
.where(eq(gameVersions.id, entryVersion.versionId))
|
||||
.limit(1)
|
||||
|
||||
if (gv) {
|
||||
const [existingSupport] = await db
|
||||
.select()
|
||||
.from(gamePlatformSupport)
|
||||
.where(
|
||||
and(
|
||||
eq(gamePlatformSupport.gameId, gv.gameId),
|
||||
eq(gamePlatformSupport.hardwareSlug, updated.hardwareSlug),
|
||||
),
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
if (existingSupport) {
|
||||
await db
|
||||
.update(gamePlatformSupport)
|
||||
.set({
|
||||
antiCheatRelevant:
|
||||
body.antiCheatRelevant !== undefined
|
||||
? body.antiCheatRelevant
|
||||
: existingSupport.antiCheatRelevant,
|
||||
antiCheatName:
|
||||
body.antiCheatName !== undefined
|
||||
? body.antiCheatName
|
||||
: existingSupport.antiCheatName,
|
||||
antiCheatStatus:
|
||||
body.antiCheatStatus !== undefined
|
||||
? (body.antiCheatStatus ?? "unknown")
|
||||
: existingSupport.antiCheatStatus,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(gamePlatformSupport.id, existingSupport.id))
|
||||
} else {
|
||||
await db.insert(gamePlatformSupport).values({
|
||||
gameId: gv.gameId,
|
||||
hardwareSlug: updated.hardwareSlug,
|
||||
isSupported: true,
|
||||
protonStatus: "unknown",
|
||||
antiCheatRelevant: body.antiCheatRelevant ?? false,
|
||||
antiCheatName: body.antiCheatName ?? null,
|
||||
antiCheatStatus: (body.antiCheatStatus ?? "unknown"),
|
||||
playabilityStatus: "unknown",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updated
|
||||
},
|
||||
{
|
||||
params: t.Object({ id: t.String() }),
|
||||
body: t.Object({
|
||||
fpsAvg: t.Optional(t.Number()),
|
||||
fpsOnePercentLow: t.Optional(t.Number()),
|
||||
fpsLow: t.Optional(t.Number()),
|
||||
fpsHigh: t.Optional(t.Number()),
|
||||
fpsAvg: t.Optional(t.Union([t.Number(), t.Null()])),
|
||||
fpsOnePercentLow: t.Optional(t.Union([t.Number(), t.Null()])),
|
||||
fpsLow: t.Optional(t.Union([t.Number(), t.Null()])),
|
||||
fpsHigh: t.Optional(t.Union([t.Number(), t.Null()])),
|
||||
protonVersion: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
osVersion: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
upscalerType: t.Optional(
|
||||
@@ -331,6 +398,17 @@ export const performanceVerifyRoutes = new Elysia({
|
||||
launchOptions: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
settingsJson: t.Optional(t.Union([t.Array(t.Any()), t.Null()])),
|
||||
userNotes: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
antiCheatRelevant: t.Optional(t.Boolean()),
|
||||
antiCheatName: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
antiCheatStatus: t.Optional(
|
||||
t.Union([
|
||||
t.Literal("none"),
|
||||
t.Literal("supported"),
|
||||
t.Literal("unsupported"),
|
||||
t.Literal("unknown"),
|
||||
t.Null(),
|
||||
]),
|
||||
),
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
gameComments,
|
||||
} from "@/lib/db/schema"
|
||||
import { ilike, or, sql, eq, inArray, and } from "drizzle-orm"
|
||||
import { fuzzySearchTerm } from "@/lib/db/search"
|
||||
|
||||
interface SteamSearchItem {
|
||||
id: number
|
||||
@@ -31,6 +32,7 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
return { error: "Query must be at least 2 characters" }
|
||||
}
|
||||
|
||||
const titleTerm = fuzzySearchTerm(query.q)
|
||||
const term = `%${query.q}%`
|
||||
|
||||
// ── 1. Search local database ────────────────────────────────────
|
||||
@@ -39,7 +41,7 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
.from(games)
|
||||
.where(
|
||||
or(
|
||||
ilike(games.title, term),
|
||||
ilike(games.title, titleTerm),
|
||||
ilike(games.developer, term),
|
||||
ilike(games.publisher, term),
|
||||
),
|
||||
@@ -281,6 +283,7 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
genres: g.genres,
|
||||
source: g.source,
|
||||
counts,
|
||||
platforms: g.platforms,
|
||||
platformSupport: platform
|
||||
? {
|
||||
isSupported: platform.isSupported,
|
||||
@@ -289,7 +292,19 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
antiCheatName: platform.antiCheatName,
|
||||
antiCheatStatus: platform.antiCheatStatus,
|
||||
}
|
||||
: null,
|
||||
: g.platforms
|
||||
? {
|
||||
isSupported: g.platforms.linux || g.platforms.windows || false,
|
||||
protonStatus: g.platforms.linux
|
||||
? "native"
|
||||
: g.platforms.windows
|
||||
? "proton"
|
||||
: "unsupported",
|
||||
antiCheatRelevant: false,
|
||||
antiCheatName: null,
|
||||
antiCheatStatus: "unknown",
|
||||
}
|
||||
: null,
|
||||
isRawPerformer: rawPerformerMap.get(g.id) ?? false,
|
||||
isPoorPerformance: poorPerformerMap.get(g.id) ?? false,
|
||||
bestFps: bestFpsMap.get(g.id) ?? null,
|
||||
|
||||
Reference in New Issue
Block a user