@@ -888,21 +1047,19 @@ export function GamePageClient({
ref={presetsRef}
className="flex gap-4 overflow-x-auto scrollbar-hide pb-4 px-1"
>
- {[...filteredPresets]
- .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
- .map((preset) => {
- const raw = isRawPerformerPreset(preset)
- const fpsColor = getFpsColor(preset)
- return (
- handlePresetOpen(preset.id)}
- className={`shrink-0 w-72 flex flex-col gap-3 p-4 rounded-xl border transition-colors cursor-pointer hover:border-primary/30 ${
- raw
- ? "border-green-500/30 bg-green-500/5"
- : "border-border bg-text/3"
- }`}
- >
+ {regularPresets.map((preset) => {
+ const raw = isRawPerformerPreset(preset)
+ const fpsColor = getFpsColor(preset)
+ return (
+ handlePresetOpen(preset.id)}
+ className={`shrink-0 w-72 flex flex-col gap-3 p-4 rounded-xl border transition-colors cursor-pointer hover:border-primary/30 ${
+ raw
+ ? "border-green-500/30 bg-green-500/5"
+ : "border-border bg-text/3"
+ }`}
+ >
{/* Header */}
diff --git a/app/game/[id]/page.tsx b/app/game/[id]/page.tsx
index 91e42c8..99d112b 100644
--- a/app/game/[id]/page.tsx
+++ b/app/game/[id]/page.tsx
@@ -233,6 +233,8 @@ export default async function GamePage({
launchOptions: performanceEntries.launchOptions,
userNotes: performanceEntries.userNotes,
verifiedAt: performanceEntries.verifiedAt,
+ isPinned: performanceEntries.isPinned,
+ pinnedAt: performanceEntries.pinnedAt,
})
.from(performanceEntries)
.innerJoin(gameVersions, eq(performanceEntries.versionId, gameVersions.id))
@@ -245,7 +247,7 @@ export default async function GamePage({
sql`${performanceEntries.settingsJson} IS NOT NULL`,
),
)
- .orderBy(desc(performanceEntries.upvotes)),
+ .orderBy(desc(performanceEntries.isPinned), desc(performanceEntries.upvotes)),
])
// โโ Sync logic: force or stale-while-revalidate โโโโโโโโโโโโโโโโโ
@@ -319,6 +321,8 @@ export default async function GamePage({
userImage: p.userImage,
downvotes: p.downvotes,
verifiedAt: p.verifiedAt ? p.verifiedAt.toISOString() : null,
+ isPinned: p.isPinned,
+ pinnedAt: p.pinnedAt ? p.pinnedAt.toISOString() : null,
}))
return (
diff --git a/app/game/[id]/preset-detail-modal.tsx b/app/game/[id]/preset-detail-modal.tsx
index 5209d4a..1a78286 100644
--- a/app/game/[id]/preset-detail-modal.tsx
+++ b/app/game/[id]/preset-detail-modal.tsx
@@ -45,6 +45,8 @@ interface Preset {
userName: string | null
userImage: string | null
verifiedAt: string | null
+ isPinned: boolean
+ pinnedAt: string | null
createdAt: string
}
@@ -156,6 +158,18 @@ export function PresetDetailModal({
setReportDetails("")
}
+ const handleTogglePin = async () => {
+ const method = preset.isPinned ? "DELETE" : "POST"
+ try {
+ const res = await fetch(`/api/performance/${preset.id}/pin`, { method })
+ if (res.ok) {
+ router.refresh()
+ }
+ } catch (err) {
+ console.error("Failed to toggle pin:", err)
+ }
+ }
+
return (
<>
@@ -338,6 +352,14 @@ export function PresetDetailModal({
{/* Actions */}
+ {isAdmin && (
+
+ )}
{(isOwner || isAdmin) && (
<>
{!showDeleteConfirm ? (
diff --git a/drizzle/0012_clever_thunderbolt_ross.sql b/drizzle/0012_clever_thunderbolt_ross.sql
new file mode 100644
index 0000000..cd48ca0
--- /dev/null
+++ b/drizzle/0012_clever_thunderbolt_ross.sql
@@ -0,0 +1,2 @@
+ALTER TABLE "performance_entries" ADD COLUMN "is_pinned" boolean DEFAULT false NOT NULL;--> statement-breakpoint
+ALTER TABLE "performance_entries" ADD COLUMN "pinned_at" timestamp;
\ No newline at end of file
diff --git a/drizzle/meta/0012_snapshot.json b/drizzle/meta/0012_snapshot.json
new file mode 100644
index 0000000..f62d01f
--- /dev/null
+++ b/drizzle/meta/0012_snapshot.json
@@ -0,0 +1,1720 @@
+{
+ "id": "9ad774e3-fb62-48e7-9b48-69fd0776b46d",
+ "prevId": "89fabc81-6eae-4f75-b7b5-668c8f9c82c6",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "account_userId_idx": {
+ "name": "account_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.passkey": {
+ "name": "passkey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public_key": {
+ "name": "public_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "counter": {
+ "name": "counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backed_up": {
+ "name": "backed_up",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aaguid": {
+ "name": "aaguid",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "passkey_userId_idx": {
+ "name": "passkey_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "passkey_credentialID_idx": {
+ "name": "passkey_credentialID_idx",
+ "columns": [
+ {
+ "expression": "credential_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "passkey_user_id_user_id_fk": {
+ "name": "passkey_user_id_user_id_fk",
+ "tableFrom": "passkey",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "impersonated_by": {
+ "name": "impersonated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "session_userId_idx": {
+ "name": "session_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "banned": {
+ "name": "banned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "ban_reason": {
+ "name": "ban_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ban_expires": {
+ "name": "ban_expires",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_login_method": {
+ "name": "last_login_method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.game_comments": {
+ "name": "game_comments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "game_id": {
+ "name": "game_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "content": {
+ "name": "content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upvotes": {
+ "name": "upvotes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_removed": {
+ "name": "is_removed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comments_game_created_idx": {
+ "name": "comments_game_created_idx",
+ "columns": [
+ {
+ "expression": "game_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comments_parent_idx": {
+ "name": "comments_parent_idx",
+ "columns": [
+ {
+ "expression": "parent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "game_comments_game_id_games_id_fk": {
+ "name": "game_comments_game_id_games_id_fk",
+ "tableFrom": "game_comments",
+ "tableTo": "games",
+ "columnsFrom": [
+ "game_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "game_comments_user_id_user_id_fk": {
+ "name": "game_comments_user_id_user_id_fk",
+ "tableFrom": "game_comments",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "game_comments_parent_id_game_comments_id_fk": {
+ "name": "game_comments_parent_id_game_comments_id_fk",
+ "tableFrom": "game_comments",
+ "tableTo": "game_comments",
+ "columnsFrom": [
+ "parent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.game_platform_support": {
+ "name": "game_platform_support",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "game_id": {
+ "name": "game_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hardware_slug": {
+ "name": "hardware_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_supported": {
+ "name": "is_supported",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "proton_status": {
+ "name": "proton_status",
+ "type": "proton_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "anti_cheat_relevant": {
+ "name": "anti_cheat_relevant",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "anti_cheat_name": {
+ "name": "anti_cheat_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "anti_cheat_version": {
+ "name": "anti_cheat_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "anti_cheat_status": {
+ "name": "anti_cheat_status",
+ "type": "anti_cheat_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "game_platform_support_game_id_games_id_fk": {
+ "name": "game_platform_support_game_id_games_id_fk",
+ "tableFrom": "game_platform_support",
+ "tableTo": "games",
+ "columnsFrom": [
+ "game_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "game_platform_support_hardware_slug_hardware_slug_fk": {
+ "name": "game_platform_support_hardware_slug_hardware_slug_fk",
+ "tableFrom": "game_platform_support",
+ "tableTo": "hardware",
+ "columnsFrom": [
+ "hardware_slug"
+ ],
+ "columnsTo": [
+ "slug"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "game_hardware_unique": {
+ "name": "game_hardware_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "game_id",
+ "hardware_slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.game_versions": {
+ "name": "game_versions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "game_id": {
+ "name": "game_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "build_id": {
+ "name": "build_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "version_string": {
+ "name": "version_string",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_latest": {
+ "name": "is_latest",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "game_versions_game_id_games_id_fk": {
+ "name": "game_versions_game_id_games_id_fk",
+ "tableFrom": "game_versions",
+ "tableTo": "games",
+ "columnsFrom": [
+ "game_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "game_build_unique": {
+ "name": "game_build_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "game_id",
+ "build_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.games": {
+ "name": "games",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "steam_app_id": {
+ "name": "steam_app_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "game_source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'steam'"
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "publisher": {
+ "name": "publisher",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "developer": {
+ "name": "developer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "genres": {
+ "name": "genres",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "header_image": {
+ "name": "header_image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "capsule_image": {
+ "name": "capsule_image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "store_url": {
+ "name": "store_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "online_multiplayer_status": {
+ "name": "online_multiplayer_status",
+ "type": "online_multiplayer_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "system_requirements": {
+ "name": "system_requirements",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metacritic_score": {
+ "name": "metacritic_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metacritic_url": {
+ "name": "metacritic_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "recommendations_total": {
+ "name": "recommendations_total",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "price_current": {
+ "name": "price_current",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "price_initial": {
+ "name": "price_initial",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "price_currency": {
+ "name": "price_currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_free": {
+ "name": "is_free",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "release_date": {
+ "name": "release_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "categories": {
+ "name": "categories",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platforms": {
+ "name": "platforms",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_sync": {
+ "name": "last_sync",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_status": {
+ "name": "sync_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'pending'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "games_source_idx": {
+ "name": "games_source_idx",
+ "columns": [
+ {
+ "expression": "source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "games_steam_app_id_unique": {
+ "name": "games_steam_app_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "steam_app_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.hardware": {
+ "name": "hardware",
+ "schema": "",
+ "columns": {
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "device_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.performance_entries": {
+ "name": "performance_entries",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "version_id": {
+ "name": "version_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hardware_slug": {
+ "name": "hardware_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fps_avg": {
+ "name": "fps_avg",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fps_low": {
+ "name": "fps_low",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fps_one_percent_low": {
+ "name": "fps_one_percent_low",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fps_high": {
+ "name": "fps_high",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "proton_version": {
+ "name": "proton_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "os_version": {
+ "name": "os_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "upscaler_type": {
+ "name": "upscaler_type",
+ "type": "upscaler_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "upscaler_version": {
+ "name": "upscaler_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "frame_gen_method": {
+ "name": "frame_gen_method",
+ "type": "frame_gen_method",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "load_time_ssd": {
+ "name": "load_time_ssd",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "load_time_sd": {
+ "name": "load_time_sd",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "launch_options": {
+ "name": "launch_options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "settings_json": {
+ "name": "settings_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_notes": {
+ "name": "user_notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "estimated_battery_min": {
+ "name": "estimated_battery_min",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "custom_system": {
+ "name": "custom_system",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_removed": {
+ "name": "is_removed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_pinned": {
+ "name": "is_pinned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "pinned_at": {
+ "name": "pinned_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "removed_reason": {
+ "name": "removed_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "upvotes": {
+ "name": "upvotes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "downvotes": {
+ "name": "downvotes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "verified_at": {
+ "name": "verified_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "verified_by": {
+ "name": "verified_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "perf_hardware_upscaler_idx": {
+ "name": "perf_hardware_upscaler_idx",
+ "columns": [
+ {
+ "expression": "hardware_slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "upscaler_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "perf_version_idx": {
+ "name": "perf_version_idx",
+ "columns": [
+ {
+ "expression": "version_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "perf_user_idx": {
+ "name": "perf_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "performance_entries_version_id_game_versions_id_fk": {
+ "name": "performance_entries_version_id_game_versions_id_fk",
+ "tableFrom": "performance_entries",
+ "tableTo": "game_versions",
+ "columnsFrom": [
+ "version_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "performance_entries_hardware_slug_hardware_slug_fk": {
+ "name": "performance_entries_hardware_slug_hardware_slug_fk",
+ "tableFrom": "performance_entries",
+ "tableTo": "hardware",
+ "columnsFrom": [
+ "hardware_slug"
+ ],
+ "columnsTo": [
+ "slug"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "performance_entries_user_id_user_id_fk": {
+ "name": "performance_entries_user_id_user_id_fk",
+ "tableFrom": "performance_entries",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "performance_entries_verified_by_user_id_fk": {
+ "name": "performance_entries_verified_by_user_id_fk",
+ "tableFrom": "performance_entries",
+ "tableTo": "user",
+ "columnsFrom": [
+ "verified_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.saved_games": {
+ "name": "saved_games",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "game_id": {
+ "name": "game_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "saved_games_user_id_user_id_fk": {
+ "name": "saved_games_user_id_user_id_fk",
+ "tableFrom": "saved_games",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "saved_games_game_id_games_id_fk": {
+ "name": "saved_games_game_id_games_id_fk",
+ "tableFrom": "saved_games",
+ "tableTo": "games",
+ "columnsFrom": [
+ "game_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "saved_games_user_game_unique": {
+ "name": "saved_games_user_game_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id",
+ "game_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.reports": {
+ "name": "reports",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "entry_id": {
+ "name": "entry_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reporter_id": {
+ "name": "reporter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "report_reason",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "details": {
+ "name": "details",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "report_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'open'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "reports_entry_reporter_unique": {
+ "name": "reports_entry_reporter_unique",
+ "columns": [
+ {
+ "expression": "entry_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "reporter_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "reports_entry_id_performance_entries_id_fk": {
+ "name": "reports_entry_id_performance_entries_id_fk",
+ "tableFrom": "reports",
+ "tableTo": "performance_entries",
+ "columnsFrom": [
+ "entry_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "reports_reporter_id_user_id_fk": {
+ "name": "reports_reporter_id_user_id_fk",
+ "tableFrom": "reports",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reporter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.anti_cheat_status": {
+ "name": "anti_cheat_status",
+ "schema": "public",
+ "values": [
+ "none",
+ "supported",
+ "unsupported",
+ "unknown"
+ ]
+ },
+ "public.proton_status": {
+ "name": "proton_status",
+ "schema": "public",
+ "values": [
+ "native",
+ "proton",
+ "unsupported",
+ "unknown"
+ ]
+ },
+ "public.game_source": {
+ "name": "game_source",
+ "schema": "public",
+ "values": [
+ "steam",
+ "manual",
+ "gog",
+ "epic"
+ ]
+ },
+ "public.online_multiplayer_status": {
+ "name": "online_multiplayer_status",
+ "schema": "public",
+ "values": [
+ "none",
+ "supported",
+ "unknown"
+ ]
+ },
+ "public.device_type": {
+ "name": "device_type",
+ "schema": "public",
+ "values": [
+ "handheld",
+ "console"
+ ]
+ },
+ "public.frame_gen_method": {
+ "name": "frame_gen_method",
+ "schema": "public",
+ "values": [
+ "none",
+ "fsr_fg",
+ "dlss_fg",
+ "lsfg",
+ "other"
+ ]
+ },
+ "public.upscaler_type": {
+ "name": "upscaler_type",
+ "schema": "public",
+ "values": [
+ "none",
+ "fsr",
+ "dlss",
+ "xess",
+ "lsfg",
+ "other"
+ ]
+ },
+ "public.report_reason": {
+ "name": "report_reason",
+ "schema": "public",
+ "values": [
+ "inaccurate",
+ "spam",
+ "inappropriate",
+ "other"
+ ]
+ },
+ "public.report_status": {
+ "name": "report_status",
+ "schema": "public",
+ "values": [
+ "open",
+ "reviewed",
+ "dismissed"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index 6a26915..062fac3 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -85,6 +85,13 @@
"when": 1777389114548,
"tag": "0011_talented_bloodstorm",
"breakpoints": true
+ },
+ {
+ "idx": 12,
+ "version": "7",
+ "when": 1777389996351,
+ "tag": "0012_clever_thunderbolt_ross",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/lib/api/performance.ts b/lib/api/performance.ts
index 3d6f41d..21965d1 100644
--- a/lib/api/performance.ts
+++ b/lib/api/performance.ts
@@ -138,6 +138,42 @@ export const performanceVerifyRoutes = new Elysia({
params: t.Object({ id: t.String() }),
},
)
+ // โโ Pin a preset (admin only) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ .post(
+ "/:id/pin",
+ async ({ request, params, set }) => {
+ const guard = await requireRole(request.headers, ["admin"])
+ if (!guard.ok) { set.status = guard.status; return { error: guard.error } }
+
+ const [entry] = await db
+ .update(performanceEntries)
+ .set({ isPinned: true, pinnedAt: new Date() })
+ .where(eq(performanceEntries.id, params.id))
+ .returning()
+
+ if (!entry) { set.status = 404; return { error: "Entry not found" } }
+ return { entry }
+ },
+ { params: t.Object({ id: t.String() }) },
+ )
+ // โโ Unpin a preset (admin only) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ .delete(
+ "/:id/pin",
+ async ({ request, params, set }) => {
+ const guard = await requireRole(request.headers, ["admin"])
+ if (!guard.ok) { set.status = guard.status; return { error: guard.error } }
+
+ const [entry] = await db
+ .update(performanceEntries)
+ .set({ isPinned: false, pinnedAt: null })
+ .where(eq(performanceEntries.id, params.id))
+ .returning()
+
+ if (!entry) { set.status = 404; return { error: "Entry not found" } }
+ return { entry }
+ },
+ { params: t.Object({ id: t.String() }) },
+ )
// โโ User-scoped soft delete (owner or admin) โโโโโโโโโโโโโโโโโโโโ
.delete(
"/:id/user-delete",
diff --git a/lib/db/schema/performanceEntries.ts b/lib/db/schema/performanceEntries.ts
index b20d0f7..4078268 100644
--- a/lib/db/schema/performanceEntries.ts
+++ b/lib/db/schema/performanceEntries.ts
@@ -87,6 +87,8 @@ export const performanceEntries = pgTable(
// Moderation
isRemoved: boolean("is_removed").default(false).notNull(),
+ isPinned: boolean("is_pinned").default(false).notNull(),
+ pinnedAt: timestamp("pinned_at"),
removedReason: text("removed_reason"),
// Community rating