feat: add public dashboard API routes for trending, best new, most tested, most reported
This commit is contained in:
@@ -32,6 +32,7 @@ import { communitySuggestionRoutes } from "@/lib/api/community-suggestions"
|
|||||||
import { gamesListingRoutes } from "@/lib/api/games-listing"
|
import { gamesListingRoutes } from "@/lib/api/games-listing"
|
||||||
import { steamgridProxyRoutes } from "@/lib/api/steamgrid-proxy"
|
import { steamgridProxyRoutes } from "@/lib/api/steamgrid-proxy"
|
||||||
import { dashboardRoutes } from "@/lib/api/dashboard"
|
import { dashboardRoutes } from "@/lib/api/dashboard"
|
||||||
|
import { dashboardPublicRoutes } from "@/lib/api/dashboard-public"
|
||||||
import { savedFilterRoutes } from "@/lib/api/saved-filters"
|
import { savedFilterRoutes } from "@/lib/api/saved-filters"
|
||||||
import { cronRoutes } from "@/lib/api/cron"
|
import { cronRoutes } from "@/lib/api/cron"
|
||||||
import { profilePhotoRoutes } from "@/lib/api/profile-photo"
|
import { profilePhotoRoutes } from "@/lib/api/profile-photo"
|
||||||
@@ -122,6 +123,7 @@ export const app = new Elysia({ prefix: "/api" })
|
|||||||
.use(cronRoutes)
|
.use(cronRoutes)
|
||||||
// Dashboard
|
// Dashboard
|
||||||
.use(dashboardRoutes)
|
.use(dashboardRoutes)
|
||||||
|
.use(dashboardPublicRoutes)
|
||||||
// Root
|
// Root
|
||||||
.get("/", () => ({
|
.get("/", () => ({
|
||||||
name: "DeckyVault API",
|
name: "DeckyVault API",
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import { Elysia, t } from "elysia"
|
||||||
|
import { db } from "@/lib/db/index"
|
||||||
|
import {
|
||||||
|
games,
|
||||||
|
gameVersions,
|
||||||
|
performanceEntries,
|
||||||
|
gameComments,
|
||||||
|
reports,
|
||||||
|
} from "@/lib/db/schema"
|
||||||
|
import { eq, and, gte, sql, desc, count } from "drizzle-orm"
|
||||||
|
|
||||||
|
const SEVEN_DAYS_AGO = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
|
||||||
|
const NINETY_DAYS_AGO = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000)
|
||||||
|
|
||||||
|
export const dashboardPublicRoutes = new Elysia({ prefix: "/dashboard" })
|
||||||
|
|
||||||
|
// ── Trending Games (7-day activity) ────────────────────────────────
|
||||||
|
.get(
|
||||||
|
"/trending",
|
||||||
|
async () => {
|
||||||
|
const trending = await db.execute(sql`
|
||||||
|
WITH recent_benchmarks AS (
|
||||||
|
SELECT gv.game_id, COUNT(*) AS cnt
|
||||||
|
FROM performance_entries pe
|
||||||
|
JOIN game_versions gv ON pe.version_id = gv.id
|
||||||
|
WHERE pe.is_removed = false
|
||||||
|
AND pe.created_at >= ${SEVEN_DAYS_AGO}
|
||||||
|
GROUP BY gv.game_id
|
||||||
|
),
|
||||||
|
recent_comments AS (
|
||||||
|
SELECT gc.game_id, COUNT(*) AS cnt
|
||||||
|
FROM game_comments gc
|
||||||
|
WHERE gc.is_removed = false
|
||||||
|
AND gc.created_at >= ${SEVEN_DAYS_AGO}
|
||||||
|
GROUP BY gc.game_id
|
||||||
|
),
|
||||||
|
recent_upvotes AS (
|
||||||
|
SELECT gv.game_id, SUM(pe.upvotes) AS total_upvotes
|
||||||
|
FROM performance_entries pe
|
||||||
|
JOIN game_versions gv ON pe.version_id = gv.id
|
||||||
|
WHERE pe.is_removed = false
|
||||||
|
AND pe.updated_at >= ${SEVEN_DAYS_AGO}
|
||||||
|
GROUP BY gv.game_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
g.id,
|
||||||
|
g.title,
|
||||||
|
g.capsule_image,
|
||||||
|
g.header_image,
|
||||||
|
g.playability_status,
|
||||||
|
COALESCE(rb.cnt, 0) AS benchmark_count,
|
||||||
|
COALESCE(rc.cnt, 0) AS comment_count,
|
||||||
|
COALESCE(ru.total_upvotes, 0) AS upvote_count,
|
||||||
|
(COALESCE(rb.cnt, 0) * 3 + COALESCE(rc.cnt, 0) * 2 + COALESCE(ru.total_upvotes, 0) * 1) AS activity_score
|
||||||
|
FROM games g
|
||||||
|
LEFT JOIN recent_benchmarks rb ON rb.game_id = g.id
|
||||||
|
LEFT JOIN recent_comments rc ON rc.game_id = g.id
|
||||||
|
LEFT JOIN recent_upvotes ru ON ru.game_id = g.id
|
||||||
|
WHERE (rb.cnt IS NOT NULL OR rc.cnt IS NOT NULL OR ru.total_upvotes IS NOT NULL)
|
||||||
|
ORDER BY activity_score DESC
|
||||||
|
LIMIT 10
|
||||||
|
`)
|
||||||
|
|
||||||
|
return trending.rows
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// ── Best Performing New Releases ─────────────────────────────────
|
||||||
|
.get(
|
||||||
|
"/best-new-releases",
|
||||||
|
async () => {
|
||||||
|
const results = await db.execute(sql`
|
||||||
|
SELECT
|
||||||
|
g.id,
|
||||||
|
g.title,
|
||||||
|
g.capsule_image,
|
||||||
|
g.header_image,
|
||||||
|
g.release_date,
|
||||||
|
g.created_at,
|
||||||
|
g.playability_status,
|
||||||
|
AVG(pe.fps_avg) AS avg_fps,
|
||||||
|
COUNT(pe.id) AS benchmark_count
|
||||||
|
FROM games g
|
||||||
|
JOIN game_versions gv ON gv.game_id = g.id
|
||||||
|
JOIN performance_entries pe ON pe.version_id = gv.id
|
||||||
|
WHERE pe.is_removed = false
|
||||||
|
AND (g.created_at >= ${new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)}
|
||||||
|
OR g.release_date IS NOT NULL)
|
||||||
|
GROUP BY g.id, g.title, g.capsule_image, g.header_image, g.release_date, g.created_at, g.playability_status
|
||||||
|
HAVING COUNT(pe.id) >= 3
|
||||||
|
ORDER BY avg_fps DESC
|
||||||
|
LIMIT 10
|
||||||
|
`)
|
||||||
|
|
||||||
|
return results.rows
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// ── Most Tested Games ─────────────────────────────────────────────
|
||||||
|
.get(
|
||||||
|
"/most-tested",
|
||||||
|
async () => {
|
||||||
|
const results = await db.execute(sql`
|
||||||
|
SELECT
|
||||||
|
g.id,
|
||||||
|
g.title,
|
||||||
|
g.capsule_image,
|
||||||
|
g.header_image,
|
||||||
|
g.playability_status,
|
||||||
|
COUNT(pe.id) AS benchmark_count
|
||||||
|
FROM games g
|
||||||
|
JOIN game_versions gv ON gv.game_id = g.id
|
||||||
|
JOIN performance_entries pe ON pe.version_id = gv.id
|
||||||
|
WHERE pe.is_removed = false
|
||||||
|
GROUP BY g.id, g.title, g.capsule_image, g.header_image, g.playability_status
|
||||||
|
ORDER BY benchmark_count DESC
|
||||||
|
LIMIT 10
|
||||||
|
`)
|
||||||
|
|
||||||
|
return results.rows
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// ── Most Reported Games ────────────────────────────────────────────
|
||||||
|
.get(
|
||||||
|
"/most-reported",
|
||||||
|
async () => {
|
||||||
|
const results = await db.execute(sql`
|
||||||
|
SELECT
|
||||||
|
g.id,
|
||||||
|
g.title,
|
||||||
|
g.capsule_image,
|
||||||
|
g.header_image,
|
||||||
|
COUNT(DISTINCT r.id) AS report_count
|
||||||
|
FROM games g
|
||||||
|
JOIN game_versions gv ON gv.game_id = g.id
|
||||||
|
JOIN performance_entries pe ON pe.version_id = gv.id
|
||||||
|
JOIN reports r ON r.entry_id = pe.id
|
||||||
|
WHERE r.status = 'open'
|
||||||
|
GROUP BY g.id, g.title, g.capsule_image, g.header_image
|
||||||
|
ORDER BY report_count DESC
|
||||||
|
LIMIT 10
|
||||||
|
`)
|
||||||
|
|
||||||
|
return results.rows
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -22,5 +22,6 @@ export { steamReviewRoutes } from "./steam-reviews"
|
|||||||
export { communitySuggestionRoutes } from "./community-suggestions"
|
export { communitySuggestionRoutes } from "./community-suggestions"
|
||||||
export { savedFilterRoutes } from "./saved-filters"
|
export { savedFilterRoutes } from "./saved-filters"
|
||||||
export { dashboardRoutes } from "./dashboard"
|
export { dashboardRoutes } from "./dashboard"
|
||||||
|
export { dashboardPublicRoutes } from "./dashboard-public"
|
||||||
export { cronRoutes } from "./cron"
|
export { cronRoutes } from "./cron"
|
||||||
export { profilePhotoRoutes } from "./profile-photo"
|
export { profilePhotoRoutes } from "./profile-photo"
|
||||||
|
|||||||
Reference in New Issue
Block a user