feat: extend user API with contribution history and verified count
This commit is contained in:
+94
-1
@@ -1,7 +1,7 @@
|
|||||||
import { Elysia, t } from "elysia"
|
import { Elysia, t } from "elysia"
|
||||||
import { auth } from "@/lib/auth"
|
import { auth } from "@/lib/auth"
|
||||||
import { db } from "@/lib/db/index"
|
import { db } from "@/lib/db/index"
|
||||||
import { user, performanceEntries } from "@/lib/db/schema"
|
import { user, performanceEntries, games, gameVersions, hardware } from "@/lib/db/schema"
|
||||||
import { eq, sql } from "drizzle-orm"
|
import { eq, sql } from "drizzle-orm"
|
||||||
|
|
||||||
export const userRoutes = new Elysia({ prefix: "/user" })
|
export const userRoutes = new Elysia({ prefix: "/user" })
|
||||||
@@ -32,6 +32,14 @@ export const userRoutes = new Elysia({ prefix: "/user" })
|
|||||||
.from(performanceEntries)
|
.from(performanceEntries)
|
||||||
.where(eq(performanceEntries.userId, params.id))
|
.where(eq(performanceEntries.userId, params.id))
|
||||||
|
|
||||||
|
// Count verified entries
|
||||||
|
const [{ count: verifiedEntries }] = await db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(performanceEntries)
|
||||||
|
.where(
|
||||||
|
sql`${performanceEntries.userId} = ${params.id} AND ${performanceEntries.verifiedAt} IS NOT NULL`
|
||||||
|
)
|
||||||
|
|
||||||
// Reputation = contributions * 10 (simple formula for now)
|
// Reputation = contributions * 10 (simple formula for now)
|
||||||
const reputation = contributions * 10
|
const reputation = contributions * 10
|
||||||
|
|
||||||
@@ -39,6 +47,7 @@ export const userRoutes = new Elysia({ prefix: "/user" })
|
|||||||
return {
|
return {
|
||||||
...publicProfile,
|
...publicProfile,
|
||||||
contributions,
|
contributions,
|
||||||
|
verifiedEntries,
|
||||||
reputation,
|
reputation,
|
||||||
verified: emailVerified,
|
verified: emailVerified,
|
||||||
}
|
}
|
||||||
@@ -85,10 +94,20 @@ export const userRoutes = new Elysia({ prefix: "/user" })
|
|||||||
.from(performanceEntries)
|
.from(performanceEntries)
|
||||||
.where(eq(performanceEntries.userId, session.user.id))
|
.where(eq(performanceEntries.userId, session.user.id))
|
||||||
|
|
||||||
|
// Count verified entries
|
||||||
|
const [{ count: verifiedEntries }] = await db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(performanceEntries)
|
||||||
|
.where(
|
||||||
|
sql`${performanceEntries.userId} = ${session.user.id} AND ${performanceEntries.verifiedAt} IS NOT NULL`
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...profile,
|
...profile,
|
||||||
contributions,
|
contributions,
|
||||||
|
verifiedEntries,
|
||||||
reputation: contributions * 10,
|
reputation: contributions * 10,
|
||||||
|
verified: profile.emailVerified,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -111,3 +130,77 @@ export const userRoutes = new Elysia({ prefix: "/user" })
|
|||||||
return sessions
|
return sessions
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.get(
|
||||||
|
"/profile/:id/contributions",
|
||||||
|
async ({ params, query, set }) => {
|
||||||
|
const [profile] = await db
|
||||||
|
.select({ id: user.id })
|
||||||
|
.from(user)
|
||||||
|
.where(eq(user.id, params.id))
|
||||||
|
.limit(1)
|
||||||
|
|
||||||
|
if (!profile) {
|
||||||
|
set.status = 404
|
||||||
|
return { error: "User not found" }
|
||||||
|
}
|
||||||
|
|
||||||
|
const limit = Math.min(Number(query.limit) || 10, 50)
|
||||||
|
const offset = Number(query.offset) || 0
|
||||||
|
|
||||||
|
const entries = await db
|
||||||
|
.select({
|
||||||
|
id: performanceEntries.id,
|
||||||
|
fpsAvg: performanceEntries.fpsAvg,
|
||||||
|
fpsLow: performanceEntries.fpsLow,
|
||||||
|
fpsHigh: performanceEntries.fpsHigh,
|
||||||
|
hardwareSlug: performanceEntries.hardwareSlug,
|
||||||
|
hardwareName: hardware.name,
|
||||||
|
fsrVersion: performanceEntries.fsrVersion,
|
||||||
|
frameGenMethod: performanceEntries.frameGenMethod,
|
||||||
|
verifiedAt: performanceEntries.verifiedAt,
|
||||||
|
createdAt: performanceEntries.createdAt,
|
||||||
|
gameTitle: games.title,
|
||||||
|
gameId: games.id,
|
||||||
|
gameHeaderImage: games.headerImage,
|
||||||
|
})
|
||||||
|
.from(performanceEntries)
|
||||||
|
.innerJoin(
|
||||||
|
gameVersions,
|
||||||
|
eq(performanceEntries.versionId, gameVersions.id),
|
||||||
|
)
|
||||||
|
.innerJoin(games, eq(gameVersions.gameId, games.id))
|
||||||
|
.innerJoin(
|
||||||
|
hardware,
|
||||||
|
eq(performanceEntries.hardwareSlug, hardware.slug),
|
||||||
|
)
|
||||||
|
.where(eq(performanceEntries.userId, params.id))
|
||||||
|
.orderBy(sql`${performanceEntries.createdAt} DESC`)
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset)
|
||||||
|
|
||||||
|
const [{ count: total }] = await db
|
||||||
|
.select({ count: sql<number>`count(*)::int` })
|
||||||
|
.from(performanceEntries)
|
||||||
|
.where(eq(performanceEntries.userId, params.id))
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: entries.map((e) => ({
|
||||||
|
...e,
|
||||||
|
createdAt: e.createdAt.toISOString(),
|
||||||
|
verifiedAt: e.verifiedAt?.toISOString() ?? null,
|
||||||
|
})),
|
||||||
|
total,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
params: t.Object({
|
||||||
|
id: t.String(),
|
||||||
|
}),
|
||||||
|
query: t.Object({
|
||||||
|
limit: t.Optional(t.String()),
|
||||||
|
offset: t.Optional(t.String()),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,6 +4,22 @@ export type ApiError = {
|
|||||||
status: number
|
status: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ContributionEntry = {
|
||||||
|
id: string
|
||||||
|
fpsAvg: number
|
||||||
|
fpsLow: number | null
|
||||||
|
fpsHigh: number | null
|
||||||
|
hardwareSlug: string
|
||||||
|
hardwareName: string
|
||||||
|
fsrVersion: string
|
||||||
|
frameGenMethod: string
|
||||||
|
verifiedAt: string | null
|
||||||
|
createdAt: string
|
||||||
|
gameTitle: string
|
||||||
|
gameId: string
|
||||||
|
gameHeaderImage: string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type UserProfile = {
|
export type UserProfile = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -12,6 +28,7 @@ export type UserProfile = {
|
|||||||
role: string | null
|
role: string | null
|
||||||
createdAt: string
|
createdAt: string
|
||||||
contributions: number
|
contributions: number
|
||||||
|
verifiedEntries: number
|
||||||
reputation: number
|
reputation: number
|
||||||
verified: boolean
|
verified: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user