diff --git a/lib/api/crud-builder.ts b/lib/api/crud-builder.ts index f276572..0b9977d 100644 --- a/lib/api/crud-builder.ts +++ b/lib/api/crud-builder.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { Elysia, t } from "elysia" import { db } from "@/lib/db/index" import { getTableColumns } from "drizzle-orm" @@ -44,6 +45,7 @@ export type CrudAuthConfig = { * @param config.filter - Exact-match filter configuration * @param config.name - Human-readable name for error messages * @param config.primaryKey - Column name used as primary key (default: "id") + * @param config.paramName - URL parameter name (defaults to primaryKey) * @param config.softDelete - If true, DELETE sets isRemoved=true instead of deleting */ export function createCrudRoutes( @@ -55,6 +57,7 @@ export function createCrudRoutes( filter?: CrudFilterConfig name?: string primaryKey?: string + paramName?: string softDelete?: boolean }, ) { @@ -65,6 +68,7 @@ export function createCrudRoutes( filter, name = "resource", primaryKey = "id", + paramName = primaryKey, softDelete = false, } = config @@ -152,9 +156,9 @@ export function createCrudRoutes( // ── GET BY ID ───────────────────────────────────────────────────── routes.get( - `/:${primaryKey}`, + `/:${paramName}`, async ({ params, set }) => { - const id = (params as any)[primaryKey] + const id = (params as any)[paramName] const [record] = await db .select() @@ -171,7 +175,7 @@ export function createCrudRoutes( }, { params: t.Object({ - [primaryKey]: t.String(), + [paramName]: t.String(), }), }, ) @@ -206,7 +210,7 @@ export function createCrudRoutes( // ── UPDATE ──────────────────────────────────────────────────────── routes.patch( - `/:${primaryKey}`, + `/:${paramName}`, async ({ params, body, request, set }) => { const roleMap: Record = { user: ["user", "contributor", "admin"], @@ -221,7 +225,7 @@ export function createCrudRoutes( return { error: guard.error } } - const id = (params as any)[primaryKey] + const id = (params as any)[paramName] // Add updatedAt if column exists const updateData = columns["updatedAt"] @@ -243,7 +247,7 @@ export function createCrudRoutes( }, { params: t.Object({ - [primaryKey]: t.String(), + [paramName]: t.String(), }), body: t.Record(t.String(), t.Any()), }, @@ -251,7 +255,7 @@ export function createCrudRoutes( // ── DELETE ──────────────────────────────────────────────────────── routes.delete( - `/:${primaryKey}`, + `/:${paramName}`, async ({ params, request, set }) => { const guard = await requireRole(request.headers, [authConfig.delete]) @@ -260,7 +264,7 @@ export function createCrudRoutes( return { error: guard.error } } - const id = (params as any)[primaryKey] + const id = (params as any)[paramName] if (softDelete && columns["isRemoved"]) { const [updated] = (await db @@ -291,7 +295,7 @@ export function createCrudRoutes( }, { params: t.Object({ - [primaryKey]: t.String(), + [paramName]: t.String(), }), }, ) diff --git a/lib/api/games.ts b/lib/api/games.ts index 146a86e..388a52d 100644 --- a/lib/api/games.ts +++ b/lib/api/games.ts @@ -12,6 +12,7 @@ export const gamesRoutes = createCrudRoutes(games, { auth: { read: "public", write: "contributor", delete: "admin" }, search: { fields: ["title", "developer", "publisher"] }, filter: { fields: ["source", "onlineMultiplayerStatus", "syncStatus"] }, + paramName: "gameId", }) // ── Game Versions (nested under /games/:gameId/versions) ────────── diff --git a/lib/api/performance.ts b/lib/api/performance.ts index 329b6a7..07fb6d6 100644 --- a/lib/api/performance.ts +++ b/lib/api/performance.ts @@ -2,7 +2,7 @@ import { Elysia, t } from "elysia" import { createCrudRoutes } from "./crud-builder" import { performanceEntries, games, gameVersions } from "@/lib/db/schema" import { db } from "@/lib/db/index" -import { eq, and, desc, sql } from "drizzle-orm" +import { eq, and, sql } from "drizzle-orm" import { requireRole } from "@/lib/auth/guard" // ── Performance Entries CRUD ────────────────────────────────────── @@ -84,7 +84,7 @@ export const performanceVerifyRoutes = new Elysia({ conditions.push(eq(performanceEntries.hardwareSlug, hardwareSlug)) } if (fsrVersion) { - conditions.push(eq(performanceEntries.fsrVersion, fsrVersion as any)) + conditions.push(eq(performanceEntries.fsrVersion, fsrVersion as any)) // eslint-disable-line @typescript-eslint/no-explicit-any } // Join through gameVersions to get to games diff --git a/lib/api/presets.ts b/lib/api/presets.ts index 5b05186..179115f 100644 --- a/lib/api/presets.ts +++ b/lib/api/presets.ts @@ -12,11 +12,12 @@ export const presetsRoutes = createCrudRoutes(communityPresets, { auth: { read: "public", write: "user", delete: "admin" }, search: { fields: ["name", "description"] }, filter: { fields: ["gameId", "hardwareSlug"] }, + paramName: "presetId", }) // ── Upvote endpoint ─────────────────────────────────────────────── export const presetUpvoteRoutes = new Elysia({ prefix: "/presets" }).post( - "/:id/upvote", + "/:presetId/upvote", async ({ params, request, set }) => { const guard = await requireRole(request.headers, [ "user", @@ -31,7 +32,7 @@ export const presetUpvoteRoutes = new Elysia({ prefix: "/presets" }).post( const [preset] = await db .select() .from(communityPresets) - .where(eq(communityPresets.id, params.id)) + .where(eq(communityPresets.id, params.presetId)) .limit(1) if (!preset) { @@ -45,13 +46,13 @@ export const presetUpvoteRoutes = new Elysia({ prefix: "/presets" }).post( upvotes: sql`${communityPresets.upvotes} + 1`, updatedAt: new Date(), }) - .where(eq(communityPresets.id, params.id)) + .where(eq(communityPresets.id, params.presetId)) .returning() return updated }, { - params: t.Object({ id: t.String() }), + params: t.Object({ presetId: t.String() }), }, ) diff --git a/lib/api/settings.ts b/lib/api/settings.ts index 7ba3db7..b3d6a3a 100644 --- a/lib/api/settings.ts +++ b/lib/api/settings.ts @@ -1,8 +1,8 @@ -import { Elysia, t } from "elysia" +import { Elysia } from "elysia" import { createCrudRoutes } from "./crud-builder" import { settingCategories, settingDefinitions } from "@/lib/db/schema" import { db } from "@/lib/db/index" -import { eq, asc } from "drizzle-orm" +import { asc } from "drizzle-orm" // ── Setting Categories CRUD ─────────────────────────────────────── export const settingCategoriesRoutes = createCrudRoutes(settingCategories, { diff --git a/lib/db/schema/gameComments.ts b/lib/db/schema/gameComments.ts index 7020186..784f721 100644 --- a/lib/db/schema/gameComments.ts +++ b/lib/db/schema/gameComments.ts @@ -22,7 +22,7 @@ export const gameComments = pgTable( userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), - parentId: text("parent_id").references((): any => gameComments.id, { + parentId: text("parent_id").references((): any => gameComments.id, { // eslint-disable-line @typescript-eslint/no-explicit-any onDelete: "cascade", }), // Tiptap JSON document