fix(api): fix Response.clone body consumed error on /api/openapi
Add embedSpec: true to openapi config to prevent cached Response body from being consumed on subsequent requests. Also add games-performance routes export and mobile-callback route.
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
import { Elysia, t } from "elysia"
|
||||
import { db } from "@/lib/db/index"
|
||||
import {
|
||||
gameVersions,
|
||||
performanceEntries,
|
||||
hardware,
|
||||
user,
|
||||
gamePlatformSupport,
|
||||
entryScreenshots,
|
||||
} from "@/lib/db/schema"
|
||||
import { and, desc, eq, sql } from "drizzle-orm"
|
||||
import { getR2PublicUrl } from "@/lib/storage"
|
||||
|
||||
/**
|
||||
* GET /api/games/:gameId/performance
|
||||
*
|
||||
* Returns all non-removed performance entries (presets) for a game,
|
||||
* with joined user, hardware, platform support, and screenshot data.
|
||||
* Ordered by isPinned desc, then upvotes desc.
|
||||
*/
|
||||
export const gamesPerformanceRoutes = new Elysia({
|
||||
prefix: "/games",
|
||||
detail: { tags: ["Games"] },
|
||||
}).get(
|
||||
"/:gameId/performance",
|
||||
async ({ params, set }) => {
|
||||
const { gameId } = params
|
||||
|
||||
const presetRows = await db
|
||||
.select({
|
||||
id: performanceEntries.id,
|
||||
hardwareSlug: performanceEntries.hardwareSlug,
|
||||
hardwareName: hardware.name,
|
||||
upvotes: performanceEntries.upvotes,
|
||||
settingsJson: performanceEntries.settingsJson,
|
||||
fpsAvg: performanceEntries.fpsAvg,
|
||||
fpsLow: performanceEntries.fpsLow,
|
||||
fpsHigh: performanceEntries.fpsHigh,
|
||||
fpsOnePercentLow: performanceEntries.fpsOnePercentLow,
|
||||
upscalerType: performanceEntries.upscalerType,
|
||||
upscalerVersion: performanceEntries.upscalerVersion,
|
||||
frameGenMethod: performanceEntries.frameGenMethod,
|
||||
protonVersion: performanceEntries.protonVersion,
|
||||
osVersion: performanceEntries.osVersion,
|
||||
createdAt: performanceEntries.createdAt,
|
||||
userId: performanceEntries.userId,
|
||||
userName: user.name,
|
||||
userImage: user.image,
|
||||
downvotes: performanceEntries.downvotes,
|
||||
launchOptions: performanceEntries.launchOptions,
|
||||
loadTimeSsd: performanceEntries.loadTimeSsd,
|
||||
loadTimeSd: performanceEntries.loadTimeSd,
|
||||
tdpWatts: performanceEntries.tdpWatts,
|
||||
youtubeVideoId: performanceEntries.youtubeVideoId,
|
||||
customSystem: performanceEntries.customSystem,
|
||||
userNotes: performanceEntries.userNotes,
|
||||
versionString: gameVersions.versionString,
|
||||
buildId: gameVersions.buildId,
|
||||
gameAntiCheatName: gamePlatformSupport.antiCheatName,
|
||||
gameAntiCheatStatus: gamePlatformSupport.antiCheatStatus,
|
||||
verifiedAt: performanceEntries.verifiedAt,
|
||||
isPinned: performanceEntries.isPinned,
|
||||
pinnedAt: performanceEntries.pinnedAt,
|
||||
})
|
||||
.from(performanceEntries)
|
||||
.innerJoin(
|
||||
gameVersions,
|
||||
eq(performanceEntries.versionId, gameVersions.id),
|
||||
)
|
||||
.innerJoin(
|
||||
hardware,
|
||||
eq(performanceEntries.hardwareSlug, hardware.slug),
|
||||
)
|
||||
.innerJoin(user, eq(performanceEntries.userId, user.id))
|
||||
.innerJoin(
|
||||
gamePlatformSupport,
|
||||
and(
|
||||
eq(gamePlatformSupport.gameId, gameVersions.gameId),
|
||||
eq(
|
||||
gamePlatformSupport.hardwareSlug,
|
||||
performanceEntries.hardwareSlug,
|
||||
),
|
||||
),
|
||||
)
|
||||
.where(
|
||||
and(
|
||||
eq(gameVersions.gameId, gameId),
|
||||
eq(performanceEntries.isRemoved, false),
|
||||
sql`${performanceEntries.settingsJson} IS NOT NULL`,
|
||||
),
|
||||
)
|
||||
.orderBy(
|
||||
desc(performanceEntries.isPinned),
|
||||
desc(performanceEntries.upvotes),
|
||||
)
|
||||
|
||||
if (presetRows.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const publicUrl = getR2PublicUrl()
|
||||
|
||||
// Fetch screenshots and hardware details per preset
|
||||
const presets = await Promise.all(
|
||||
presetRows.map(async (p) => {
|
||||
const screenshots = await db
|
||||
.select({
|
||||
id: entryScreenshots.id,
|
||||
storageKey: entryScreenshots.storageKey,
|
||||
orderIndex: entryScreenshots.orderIndex,
|
||||
width: entryScreenshots.width,
|
||||
height: entryScreenshots.height,
|
||||
})
|
||||
.from(entryScreenshots)
|
||||
.where(eq(entryScreenshots.entryId, p.id))
|
||||
.orderBy(entryScreenshots.orderIndex)
|
||||
|
||||
const [hw] = await db
|
||||
.select({
|
||||
wattHours: hardware.wattHours,
|
||||
deviceType: hardware.deviceType,
|
||||
})
|
||||
.from(hardware)
|
||||
.where(eq(hardware.slug, p.hardwareSlug))
|
||||
.limit(1)
|
||||
|
||||
const settingsCount = Array.isArray(p.settingsJson)
|
||||
? (p.settingsJson as Array<{ settings: unknown[] }>).reduce(
|
||||
(sum, cat) => sum + cat.settings.length,
|
||||
0,
|
||||
)
|
||||
: 0
|
||||
|
||||
return {
|
||||
id: p.id,
|
||||
gameId,
|
||||
hardwareSlug: p.hardwareSlug,
|
||||
hardwareName: p.hardwareName,
|
||||
upvotes: p.upvotes,
|
||||
downvotes: p.downvotes,
|
||||
settingsCount,
|
||||
fpsAvg: p.fpsAvg,
|
||||
fpsLow: p.fpsLow,
|
||||
fpsHigh: p.fpsHigh,
|
||||
fpsOnePercentLow: p.fpsOnePercentLow ?? null,
|
||||
upscalerType: p.upscalerType,
|
||||
upscalerVersion: p.upscalerVersion,
|
||||
frameGenMethod: p.frameGenMethod,
|
||||
protonVersion: p.protonVersion,
|
||||
osVersion: p.osVersion,
|
||||
createdAt: p.createdAt.toISOString(),
|
||||
settingsJson: p.settingsJson,
|
||||
launchOptions: p.launchOptions,
|
||||
loadTimeSsd: p.loadTimeSsd ?? null,
|
||||
loadTimeSd: p.loadTimeSd ?? null,
|
||||
tdpWatts: p.tdpWatts ?? null,
|
||||
youtubeVideoId: p.youtubeVideoId ?? null,
|
||||
screenshots: screenshots.map((ss) => ({
|
||||
id: ss.id,
|
||||
url: `${publicUrl}/${ss.storageKey}`,
|
||||
width: ss.width,
|
||||
height: ss.height,
|
||||
orderIndex: ss.orderIndex,
|
||||
})),
|
||||
hardwareWattHours: hw?.wattHours ? Number(hw.wattHours) : null,
|
||||
hardwareDeviceType: hw?.deviceType ?? null,
|
||||
customSystem: p.customSystem ?? false,
|
||||
userNotes: p.userNotes,
|
||||
versionString: p.versionString ?? null,
|
||||
buildId: p.buildId ?? null,
|
||||
gameAntiCheatName: p.gameAntiCheatName ?? null,
|
||||
gameAntiCheatStatus: p.gameAntiCheatStatus ?? null,
|
||||
userId: p.userId,
|
||||
userName: p.userName,
|
||||
userImage: p.userImage,
|
||||
verifiedAt: p.verifiedAt ? p.verifiedAt.toISOString() : null,
|
||||
isPinned: p.isPinned,
|
||||
pinnedAt: p.pinnedAt ? p.pinnedAt.toISOString() : null,
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
return presets
|
||||
},
|
||||
{
|
||||
params: t.Object({ gameId: t.String() }),
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user