feat: replace fsrVersion with upscalerType/upscalerVersion, add battery & custom system columns
This commit is contained in:
+13
-6
@@ -34,7 +34,8 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
||||
fpsAvg: performanceEntries.fpsAvg,
|
||||
fpsLow: performanceEntries.fpsLow,
|
||||
fpsHigh: performanceEntries.fpsHigh,
|
||||
fsrVersion: performanceEntries.fsrVersion,
|
||||
upscalerType: performanceEntries.upscalerType,
|
||||
upscalerVersion: performanceEntries.upscalerVersion,
|
||||
frameGenMethod: performanceEntries.frameGenMethod,
|
||||
protonVersion: performanceEntries.protonVersion,
|
||||
osVersion: performanceEntries.osVersion,
|
||||
@@ -112,10 +113,13 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
||||
const isRawPerformer = entries.some(
|
||||
(e) =>
|
||||
(e.fpsAvg ?? 0) >= 60 &&
|
||||
e.fsrVersion === "none" &&
|
||||
e.upscalerType === "none" &&
|
||||
e.frameGenMethod === "none",
|
||||
)
|
||||
|
||||
// ── 3b. Poor Performance check ─────────────────────────────────
|
||||
const isPoorPerformance = entries.some((e) => (e.fpsAvg ?? 0) < 30)
|
||||
|
||||
// ── 4. Boxplot per device ─────────────────────────────────────
|
||||
const boxplotMap = new Map<
|
||||
string,
|
||||
@@ -185,7 +189,7 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
||||
{ hardwareSlug: string; sum: number; count: number }
|
||||
>()
|
||||
for (const e of entries) {
|
||||
const key = `${e.fsrVersion}|${e.frameGenMethod}|${e.hardwareSlug}`
|
||||
const key = `${e.upscalerType}|${e.upscalerVersion ?? ''}|${e.frameGenMethod}|${e.hardwareSlug}`
|
||||
const existing = upscalerMap.get(key) || {
|
||||
hardwareSlug: e.hardwareSlug,
|
||||
sum: 0,
|
||||
@@ -198,9 +202,10 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
||||
|
||||
const upscalerStats = Array.from(upscalerMap.entries()).map(
|
||||
([key, data]) => {
|
||||
const [fsrVersion, frameGenMethod] = key.split("|")
|
||||
const [upscalerType, upscalerVersion, frameGenMethod] = key.split("|")
|
||||
return {
|
||||
fsrVersion,
|
||||
upscalerType,
|
||||
upscalerVersion: upscalerVersion || null,
|
||||
frameGenMethod,
|
||||
hardwareSlug: data.hardwareSlug,
|
||||
avgFps: Math.round((data.sum / data.count) * 10) / 10,
|
||||
@@ -220,8 +225,9 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
||||
fpsHigh: e.fpsHigh!,
|
||||
isRawPerformer:
|
||||
(e.fpsAvg ?? 0) >= 60 &&
|
||||
e.fsrVersion === "none" &&
|
||||
e.upscalerType === "none" &&
|
||||
e.frameGenMethod === "none",
|
||||
isPoorPerformer: (e.fpsAvg ?? 0) < 30,
|
||||
}))
|
||||
|
||||
// ── 8. Device breakdown ───────────────────────────────────────
|
||||
@@ -261,6 +267,7 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
||||
versionCount,
|
||||
},
|
||||
isRawPerformer,
|
||||
isPoorPerformance,
|
||||
boxplot,
|
||||
historical,
|
||||
upscalerStats,
|
||||
|
||||
+11
-10
@@ -120,7 +120,8 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
||||
fpsAvg: performanceEntries.fpsAvg,
|
||||
fpsLow: performanceEntries.fpsLow,
|
||||
fpsHigh: performanceEntries.fpsHigh,
|
||||
fsrVersion: performanceEntries.fsrVersion,
|
||||
upscalerType: performanceEntries.upscalerType,
|
||||
upscalerVersion: performanceEntries.upscalerVersion,
|
||||
frameGenMethod: performanceEntries.frameGenMethod,
|
||||
protonVersion: performanceEntries.protonVersion,
|
||||
osVersion: performanceEntries.osVersion,
|
||||
@@ -152,7 +153,7 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
||||
topGames: [],
|
||||
genreBreakdown: [],
|
||||
protonBreakdown: [],
|
||||
fsrBreakdown: [],
|
||||
upscalerBreakdown: [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,17 +251,17 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([version, count]) => ({ version, count }))
|
||||
|
||||
// ── FSR breakdown ───────────────────────────────────
|
||||
const fsrMap = new Map<string, { count: number; avgFps: number }>()
|
||||
// ── Upscaler breakdown ───────────────────────────────────
|
||||
const upscalerMap = new Map<string, { count: number; avgFps: number }>()
|
||||
for (const e of entries) {
|
||||
const key = e.fsrVersion ?? "none"
|
||||
if (!fsrMap.has(key)) fsrMap.set(key, { count: 0, avgFps: 0 })
|
||||
const f = fsrMap.get(key)!
|
||||
const key = e.upscalerType ?? "none"
|
||||
if (!upscalerMap.has(key)) upscalerMap.set(key, { count: 0, avgFps: 0 })
|
||||
const f = upscalerMap.get(key)!
|
||||
f.count++
|
||||
f.avgFps += e.fpsAvg ?? 0
|
||||
}
|
||||
const fsrBreakdown = [...fsrMap.entries()].map(([version, data]) => ({
|
||||
version,
|
||||
const upscalerBreakdown = [...upscalerMap.entries()].map(([type, data]) => ({
|
||||
upscalerType: type,
|
||||
count: data.count,
|
||||
avgFps: Math.round((data.avgFps / data.count) * 10) / 10,
|
||||
}))
|
||||
@@ -276,7 +277,7 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
||||
topGames,
|
||||
genreBreakdown,
|
||||
protonBreakdown,
|
||||
fsrBreakdown,
|
||||
upscalerBreakdown,
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -106,7 +106,10 @@ export const performanceSubmitRoutes = new Elysia({ prefix: "/performance" })
|
||||
fpsHigh: body.fpsHigh ?? null,
|
||||
protonVersion: body.protonVersion ?? null,
|
||||
osVersion: body.osVersion ?? null,
|
||||
fsrVersion: body.fsrVersion ?? "none",
|
||||
upscalerType: body.upscalerType ?? "none",
|
||||
upscalerVersion: body.upscalerVersion ?? null,
|
||||
estimatedBatteryMin: body.estimatedBatteryMin ?? null,
|
||||
customSystem: body.customSystem ?? false,
|
||||
frameGenMethod: body.frameGenMethod ?? "none",
|
||||
loadTimeSsd: body.loadTimeSsd ?? null,
|
||||
loadTimeSd: body.loadTimeSd ?? null,
|
||||
@@ -131,19 +134,26 @@ export const performanceSubmitRoutes = new Elysia({ prefix: "/performance" })
|
||||
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()])),
|
||||
fsrVersion: t.Optional(
|
||||
upscalerType: t.Optional(
|
||||
t.Union([
|
||||
t.Literal("none"),
|
||||
t.Literal("fsr1"),
|
||||
t.Literal("fsr2"),
|
||||
t.Literal("fsr3"),
|
||||
t.Literal("fsr"),
|
||||
t.Literal("dlss"),
|
||||
t.Literal("xess"),
|
||||
t.Literal("lsfg"),
|
||||
t.Literal("other"),
|
||||
]),
|
||||
),
|
||||
upscalerVersion: t.Optional(t.Union([t.String(), t.Null()])),
|
||||
estimatedBatteryMin: t.Optional(t.Union([t.Number(), t.Null()])),
|
||||
customSystem: t.Optional(t.Boolean()),
|
||||
frameGenMethod: t.Optional(
|
||||
t.Union([
|
||||
t.Literal("none"),
|
||||
t.Literal("fsr_fg"),
|
||||
t.Literal("dlss_fg"),
|
||||
t.Literal("lsfg"),
|
||||
t.Literal("other"),
|
||||
]),
|
||||
),
|
||||
loadTimeSsd: t.Optional(t.Union([t.Number(), t.Null()])),
|
||||
|
||||
+13
-7
@@ -12,7 +12,7 @@ export const performanceRoutes = createCrudRoutes(performanceEntries, {
|
||||
auth: { read: "public", write: "user", delete: "admin" },
|
||||
softDelete: true,
|
||||
search: { fields: ["userNotes"] },
|
||||
filter: { fields: ["hardwareSlug", "fsrVersion", "frameGenMethod"] },
|
||||
filter: { fields: ["hardwareSlug", "upscalerType", "upscalerVersion", "frameGenMethod"] },
|
||||
})
|
||||
|
||||
// ── Verify endpoint (admin/mod) ───────────────────────────────────
|
||||
@@ -183,7 +183,8 @@ export const performanceVerifyRoutes = new Elysia({
|
||||
fpsAvg: performanceEntries.fpsAvg,
|
||||
fpsLow: performanceEntries.fpsLow,
|
||||
fpsHigh: performanceEntries.fpsHigh,
|
||||
fsrVersion: performanceEntries.fsrVersion,
|
||||
upscalerType: performanceEntries.upscalerType,
|
||||
upscalerVersion: performanceEntries.upscalerVersion,
|
||||
frameGenMethod: performanceEntries.frameGenMethod,
|
||||
settingsJson: performanceEntries.settingsJson,
|
||||
userNotes: performanceEntries.userNotes,
|
||||
@@ -227,10 +228,11 @@ export const performanceVerifyRoutes = new Elysia({
|
||||
.get(
|
||||
"/stats",
|
||||
async ({ query, set }) => {
|
||||
const { gameId, hardwareSlug, fsrVersion } = query as {
|
||||
const { gameId, hardwareSlug, upscalerType, upscalerVersion } = query as {
|
||||
gameId?: string
|
||||
hardwareSlug?: string
|
||||
fsrVersion?: string
|
||||
upscalerType?: string
|
||||
upscalerVersion?: string
|
||||
}
|
||||
|
||||
if (!gameId) {
|
||||
@@ -246,8 +248,11 @@ export const performanceVerifyRoutes = new Elysia({
|
||||
if (hardwareSlug) {
|
||||
conditions.push(eq(performanceEntries.hardwareSlug, hardwareSlug))
|
||||
}
|
||||
if (fsrVersion) {
|
||||
conditions.push(eq(performanceEntries.fsrVersion, fsrVersion as any)) // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
if (upscalerType) {
|
||||
conditions.push(eq(performanceEntries.upscalerType, upscalerType as any)) // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
}
|
||||
if (upscalerVersion) {
|
||||
conditions.push(eq(performanceEntries.upscalerVersion, upscalerVersion))
|
||||
}
|
||||
|
||||
// Join through gameVersions to get to games
|
||||
@@ -272,7 +277,8 @@ export const performanceVerifyRoutes = new Elysia({
|
||||
query: t.Object({
|
||||
gameId: t.String(),
|
||||
hardwareSlug: t.Optional(t.String()),
|
||||
fsrVersion: t.Optional(t.String()),
|
||||
upscalerType: t.Optional(t.String()),
|
||||
upscalerVersion: t.Optional(t.String()),
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -146,8 +146,9 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
countMap.get(c.gameId)!.comments = c.count
|
||||
}
|
||||
|
||||
// ── 2b. Raw Performer + best FPS ────────────────────────────────
|
||||
// ── 2b. Raw Performer + Poor Performance + best FPS ────────────
|
||||
const rawPerformerMap = new Map<string, boolean>()
|
||||
const poorPerformerMap = new Map<string, boolean>()
|
||||
const bestFpsMap = new Map<string, number>()
|
||||
|
||||
if (localGameIds.length > 0) {
|
||||
@@ -157,9 +158,10 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
bestFps: sql<number>`MAX(${performanceEntries.fpsAvg})::real`,
|
||||
isRawPerformer: sql<boolean>`BOOL_OR(
|
||||
${performanceEntries.fpsAvg} >= 60
|
||||
AND ${performanceEntries.fsrVersion} = 'none'
|
||||
AND ${performanceEntries.upscalerType} = 'none'
|
||||
AND ${performanceEntries.frameGenMethod} = 'none'
|
||||
)`,
|
||||
isPoorPerformance: sql<boolean>`BOOL_OR(${performanceEntries.fpsAvg} < 30)`,
|
||||
})
|
||||
.from(performanceEntries)
|
||||
.innerJoin(
|
||||
@@ -177,6 +179,7 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
for (const row of perfStats) {
|
||||
bestFpsMap.set(row.gameId, row.bestFps)
|
||||
rawPerformerMap.set(row.gameId, row.isRawPerformer)
|
||||
poorPerformerMap.set(row.gameId, row.isPoorPerformance)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +276,7 @@ export const searchUnifiedRoutes = new Elysia({ prefix: "/search" }).get(
|
||||
}
|
||||
: null,
|
||||
isRawPerformer: rawPerformerMap.get(g.id) ?? false,
|
||||
isPoorPerformance: poorPerformerMap.get(g.id) ?? false,
|
||||
bestFps: bestFpsMap.get(g.id) ?? null,
|
||||
latestVersion: latestVersionMap.get(g.id) ?? null,
|
||||
})
|
||||
|
||||
+8
-6
@@ -1,7 +1,7 @@
|
||||
import { Elysia, t } from "elysia"
|
||||
import { auth } from "@/lib/auth"
|
||||
import { db } from "@/lib/db/index"
|
||||
import { user, performanceEntries, games, gameVersions, hardware, account } from "@/lib/db/schema"
|
||||
import { user, performanceEntries, games, gameVersions, hardware, account, passkey } from "@/lib/db/schema"
|
||||
import { eq, sql, and, desc } from "drizzle-orm"
|
||||
import { hashPassword } from "better-auth/crypto"
|
||||
|
||||
@@ -155,10 +155,11 @@ export const userRoutes = new Elysia({ prefix: "/user" })
|
||||
.from(account)
|
||||
.where(eq(account.userId, session.user.id))
|
||||
|
||||
// Count passkeys
|
||||
const passkeys = await auth.api.listPasskeys({
|
||||
headers: request.headers,
|
||||
})
|
||||
// Count passkeys via direct DB query (avoids auth.api.listPasskeys hanging)
|
||||
const passkeys = await db
|
||||
.select({ id: passkey.id })
|
||||
.from(passkey)
|
||||
.where(eq(passkey.userId, session.user.id))
|
||||
|
||||
// Check if user has a password (from accounts where providerId is "credential")
|
||||
const hasPassword = accounts.some((a) => a.providerId === "credential")
|
||||
@@ -256,7 +257,8 @@ export const userRoutes = new Elysia({ prefix: "/user" })
|
||||
fpsHigh: performanceEntries.fpsHigh,
|
||||
hardwareSlug: performanceEntries.hardwareSlug,
|
||||
hardwareName: hardware.name,
|
||||
fsrVersion: performanceEntries.fsrVersion,
|
||||
upscalerType: performanceEntries.upscalerType,
|
||||
upscalerVersion: performanceEntries.upscalerVersion,
|
||||
frameGenMethod: performanceEntries.frameGenMethod,
|
||||
verifiedAt: performanceEntries.verifiedAt,
|
||||
createdAt: performanceEntries.createdAt,
|
||||
|
||||
@@ -13,17 +13,21 @@ import { gameVersions } from "./gameVersions"
|
||||
import { hardware } from "./hardware"
|
||||
import { user } from "./auth"
|
||||
|
||||
export const fsrVersionEnum = pgEnum("fsr_version", [
|
||||
export const upscalerTypeEnum = pgEnum("upscaler_type", [
|
||||
"none",
|
||||
"fsr1",
|
||||
"fsr2",
|
||||
"fsr3",
|
||||
"fsr",
|
||||
"dlss",
|
||||
"xess",
|
||||
"lsfg",
|
||||
"other",
|
||||
])
|
||||
|
||||
export const frameGenMethodEnum = pgEnum("frame_gen_method", [
|
||||
"none",
|
||||
"fsr_fg",
|
||||
"dlss_fg",
|
||||
"lsfg",
|
||||
"other",
|
||||
])
|
||||
|
||||
export type GameSettingCategory = {
|
||||
@@ -56,8 +60,9 @@ export const performanceEntries = pgTable(
|
||||
protonVersion: text("proton_version"),
|
||||
osVersion: text("os_version"),
|
||||
|
||||
// Upscaler tracking (replaces isFsrEnabled boolean)
|
||||
fsrVersion: fsrVersionEnum("fsr_version").default("none").notNull(),
|
||||
// Upscaler tracking
|
||||
upscalerType: upscalerTypeEnum("upscaler_type").default("none").notNull(),
|
||||
upscalerVersion: text("upscaler_version"),
|
||||
frameGenMethod: frameGenMethodEnum("frame_gen_method")
|
||||
.default("none")
|
||||
.notNull(),
|
||||
@@ -73,6 +78,12 @@ export const performanceEntries = pgTable(
|
||||
settingsJson: jsonb("settings_json").$type<GameSettingCategory[]>(),
|
||||
userNotes: text("user_notes"),
|
||||
|
||||
// Battery estimate (minutes)
|
||||
estimatedBatteryMin: integer("estimated_battery_min"),
|
||||
|
||||
// Custom system flag
|
||||
customSystem: boolean("custom_system").default(false).notNull(),
|
||||
|
||||
// Moderation
|
||||
isRemoved: boolean("is_removed").default(false).notNull(),
|
||||
removedReason: text("removed_reason"),
|
||||
@@ -91,7 +102,7 @@ export const performanceEntries = pgTable(
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index("perf_hardware_fsr_idx").on(table.hardwareSlug, table.fsrVersion),
|
||||
index("perf_hardware_upscaler_idx").on(table.hardwareSlug, table.upscalerType),
|
||||
index("perf_version_idx").on(table.versionId),
|
||||
index("perf_user_idx").on(table.userId),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user