refactor: remove structured settings tables, use freeform JSON for presets and entries
This commit is contained in:
@@ -9,9 +9,6 @@ import {
|
|||||||
hardwareRoutes,
|
hardwareRoutes,
|
||||||
performanceRoutes,
|
performanceRoutes,
|
||||||
performanceVerifyRoutes,
|
performanceVerifyRoutes,
|
||||||
settingCategoriesRoutes,
|
|
||||||
settingDefinitionsRoutes,
|
|
||||||
settingsFullRoutes,
|
|
||||||
presetsRoutes,
|
presetsRoutes,
|
||||||
presetUpvoteRoutes,
|
presetUpvoteRoutes,
|
||||||
presetSettingsRoutes,
|
presetSettingsRoutes,
|
||||||
@@ -62,10 +59,6 @@ export const app = new Elysia({ prefix: "/api" })
|
|||||||
// Performance
|
// Performance
|
||||||
.use(performanceRoutes)
|
.use(performanceRoutes)
|
||||||
.use(performanceVerifyRoutes)
|
.use(performanceVerifyRoutes)
|
||||||
// Settings
|
|
||||||
.use(settingCategoriesRoutes)
|
|
||||||
.use(settingDefinitionsRoutes)
|
|
||||||
.use(settingsFullRoutes)
|
|
||||||
// Presets
|
// Presets
|
||||||
.use(presetsRoutes)
|
.use(presetsRoutes)
|
||||||
.use(presetUpvoteRoutes)
|
.use(presetUpvoteRoutes)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ CREATE TYPE "game_source" AS ENUM ('steam', 'manual', 'gog', 'epic');
|
|||||||
CREATE TYPE "online_multiplayer_status" AS ENUM ('none', 'supported', 'unknown');
|
CREATE TYPE "online_multiplayer_status" AS ENUM ('none', 'supported', 'unknown');
|
||||||
CREATE TYPE "fsr_version" AS ENUM ('none', 'fsr1', 'fsr2', 'fsr3');
|
CREATE TYPE "fsr_version" AS ENUM ('none', 'fsr1', 'fsr2', 'fsr3');
|
||||||
CREATE TYPE "frame_gen_method" AS ENUM ('none', 'fsr_fg', 'dlss_fg');
|
CREATE TYPE "frame_gen_method" AS ENUM ('none', 'fsr_fg', 'dlss_fg');
|
||||||
CREATE TYPE "setting_input_type" AS ENUM ('toggle', 'select', 'range', 'number');
|
|
||||||
CREATE TYPE "impact_level" AS ENUM ('minor', 'moderate', 'major');
|
|
||||||
|
|
||||||
-- Alter games table
|
-- Alter games table
|
||||||
ALTER TABLE "games" ALTER COLUMN "steam_app_id" DROP NOT NULL;
|
ALTER TABLE "games" ALTER COLUMN "steam_app_id" DROP NOT NULL;
|
||||||
@@ -35,31 +33,6 @@ CREATE INDEX "perf_user_idx" ON "performance_entries" ("user_id");
|
|||||||
-- Alter hardware table
|
-- Alter hardware table
|
||||||
ALTER TABLE "hardware" ADD COLUMN "sort_order" integer DEFAULT 0 NOT NULL;
|
ALTER TABLE "hardware" ADD COLUMN "sort_order" integer DEFAULT 0 NOT NULL;
|
||||||
|
|
||||||
-- New table: setting_categories
|
|
||||||
CREATE TABLE "setting_categories" (
|
|
||||||
"id" text PRIMARY KEY NOT NULL,
|
|
||||||
"name" text NOT NULL,
|
|
||||||
"slug" text NOT NULL,
|
|
||||||
"sort_order" integer DEFAULT 0 NOT NULL,
|
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
CONSTRAINT "setting_categories_slug_unique" UNIQUE("slug")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- New table: setting_definitions
|
|
||||||
CREATE TABLE "setting_definitions" (
|
|
||||||
"id" text PRIMARY KEY NOT NULL,
|
|
||||||
"category_id" text NOT NULL,
|
|
||||||
"name" text NOT NULL,
|
|
||||||
"slug" text NOT NULL,
|
|
||||||
"input_type" "setting_input_type" NOT NULL,
|
|
||||||
"options" jsonb,
|
|
||||||
"impact_level" "impact_level" DEFAULT 'minor' NOT NULL,
|
|
||||||
"sort_order" integer DEFAULT 0 NOT NULL,
|
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
CONSTRAINT "setting_category_slug_unique" UNIQUE("category_id","slug")
|
|
||||||
);
|
|
||||||
ALTER TABLE "setting_definitions" ADD CONSTRAINT "setting_definitions_category_id_setting_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "setting_categories"("id") ON DELETE cascade;
|
|
||||||
|
|
||||||
-- New table: community_presets
|
-- New table: community_presets
|
||||||
CREATE TABLE "community_presets" (
|
CREATE TABLE "community_presets" (
|
||||||
"id" text PRIMARY KEY NOT NULL,
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
@@ -69,6 +42,7 @@ CREATE TABLE "community_presets" (
|
|||||||
"description" text,
|
"description" text,
|
||||||
"created_by" text,
|
"created_by" text,
|
||||||
"upvotes" integer DEFAULT 0 NOT NULL,
|
"upvotes" integer DEFAULT 0 NOT NULL,
|
||||||
|
"settings_json" jsonb,
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
"updated_at" timestamp DEFAULT now() NOT NULL,
|
"updated_at" timestamp DEFAULT now() NOT NULL,
|
||||||
CONSTRAINT "preset_game_hardware_name_unique" UNIQUE("game_id","hardware_slug","name")
|
CONSTRAINT "preset_game_hardware_name_unique" UNIQUE("game_id","hardware_slug","name")
|
||||||
@@ -77,18 +51,6 @@ ALTER TABLE "community_presets" ADD CONSTRAINT "community_presets_game_id_games_
|
|||||||
ALTER TABLE "community_presets" ADD CONSTRAINT "community_presets_hardware_slug_hardware_slug_fk" FOREIGN KEY ("hardware_slug") REFERENCES "hardware"("slug") ON DELETE restrict;
|
ALTER TABLE "community_presets" ADD CONSTRAINT "community_presets_hardware_slug_hardware_slug_fk" FOREIGN KEY ("hardware_slug") REFERENCES "hardware"("slug") ON DELETE restrict;
|
||||||
ALTER TABLE "community_presets" ADD CONSTRAINT "community_presets_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "user"("id") ON DELETE set null;
|
ALTER TABLE "community_presets" ADD CONSTRAINT "community_presets_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "user"("id") ON DELETE set null;
|
||||||
|
|
||||||
-- New table: preset_settings
|
|
||||||
CREATE TABLE "preset_settings" (
|
|
||||||
"id" text PRIMARY KEY NOT NULL,
|
|
||||||
"preset_id" text NOT NULL,
|
|
||||||
"setting_definition_id" text NOT NULL,
|
|
||||||
"value" jsonb NOT NULL,
|
|
||||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
||||||
CONSTRAINT "preset_setting_unique" UNIQUE("preset_id","setting_definition_id")
|
|
||||||
);
|
|
||||||
ALTER TABLE "preset_settings" ADD CONSTRAINT "preset_settings_preset_id_community_presets_id_fk" FOREIGN KEY ("preset_id") REFERENCES "community_presets"("id") ON DELETE cascade;
|
|
||||||
ALTER TABLE "preset_settings" ADD CONSTRAINT "preset_settings_setting_definition_id_setting_definitions_id_fk" FOREIGN KEY ("setting_definition_id") REFERENCES "setting_definitions"("id") ON DELETE cascade;
|
|
||||||
|
|
||||||
-- New table: game_comments
|
-- New table: game_comments
|
||||||
CREATE TABLE "game_comments" (
|
CREATE TABLE "game_comments" (
|
||||||
"id" text PRIMARY KEY NOT NULL,
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
|||||||
@@ -3,11 +3,6 @@ export { userRoutes } from "./user"
|
|||||||
export { gamesRoutes, gameVersionsRoutes } from "./games"
|
export { gamesRoutes, gameVersionsRoutes } from "./games"
|
||||||
export { hardwareRoutes } from "./hardware"
|
export { hardwareRoutes } from "./hardware"
|
||||||
export { performanceRoutes, performanceVerifyRoutes } from "./performance"
|
export { performanceRoutes, performanceVerifyRoutes } from "./performance"
|
||||||
export {
|
|
||||||
settingCategoriesRoutes,
|
|
||||||
settingDefinitionsRoutes,
|
|
||||||
settingsFullRoutes,
|
|
||||||
} from "./settings"
|
|
||||||
export {
|
export {
|
||||||
presetsRoutes,
|
presetsRoutes,
|
||||||
presetUpvoteRoutes,
|
presetUpvoteRoutes,
|
||||||
|
|||||||
+33
-47
@@ -1,6 +1,6 @@
|
|||||||
import { Elysia, t } from "elysia"
|
import { Elysia, t } from "elysia"
|
||||||
import { createCrudRoutes } from "./crud-builder"
|
import { createCrudRoutes } from "./crud-builder"
|
||||||
import { communityPresets, presetSettings, settingDefinitions } from "@/lib/db/schema"
|
import { communityPresets } from "@/lib/db/schema"
|
||||||
import { db } from "@/lib/db/index"
|
import { db } from "@/lib/db/index"
|
||||||
import { eq, sql } from "drizzle-orm"
|
import { eq, sql } from "drizzle-orm"
|
||||||
import { requireRole } from "@/lib/auth/guard"
|
import { requireRole } from "@/lib/auth/guard"
|
||||||
@@ -57,40 +57,33 @@ export const presetUpvoteRoutes = new Elysia({ prefix: "/presets" }).post(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ── Preset Settings (nested under /presets/:presetId/settings) ────
|
// ── Preset Settings (nested under /presets/:presetId/settings) ────
|
||||||
|
// Settings are stored as freeform JSON on the preset itself.
|
||||||
export const presetSettingsRoutes = new Elysia({
|
export const presetSettingsRoutes = new Elysia({
|
||||||
prefix: "/presets/:presetId/settings",
|
prefix: "/presets/:presetId/settings",
|
||||||
})
|
})
|
||||||
// LIST all settings for a preset (with definition details)
|
// GET the settings JSON for a preset
|
||||||
.get(
|
.get(
|
||||||
"/",
|
"/",
|
||||||
async ({ params }) => {
|
async ({ params, set }) => {
|
||||||
const data = await db
|
const [preset] = await db
|
||||||
.select({
|
.select({ settingsJson: communityPresets.settingsJson })
|
||||||
id: presetSettings.id,
|
.from(communityPresets)
|
||||||
presetId: presetSettings.presetId,
|
.where(eq(communityPresets.id, params.presetId))
|
||||||
settingDefinitionId: presetSettings.settingDefinitionId,
|
.limit(1)
|
||||||
value: presetSettings.value,
|
|
||||||
settingName: settingDefinitions.name,
|
|
||||||
settingSlug: settingDefinitions.slug,
|
|
||||||
inputType: settingDefinitions.inputType,
|
|
||||||
options: settingDefinitions.options,
|
|
||||||
impactLevel: settingDefinitions.impactLevel,
|
|
||||||
})
|
|
||||||
.from(presetSettings)
|
|
||||||
.innerJoin(
|
|
||||||
settingDefinitions,
|
|
||||||
eq(presetSettings.settingDefinitionId, settingDefinitions.id),
|
|
||||||
)
|
|
||||||
.where(eq(presetSettings.presetId, params.presetId))
|
|
||||||
|
|
||||||
return data
|
if (!preset) {
|
||||||
|
set.status = 404
|
||||||
|
return { error: "Preset not found" }
|
||||||
|
}
|
||||||
|
|
||||||
|
return preset.settingsJson ?? []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
params: t.Object({ presetId: t.String() }),
|
params: t.Object({ presetId: t.String() }),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
// UPSERT settings for a preset (bulk create/update)
|
// PUT (replace) the settings JSON for a preset
|
||||||
.post(
|
.put(
|
||||||
"/",
|
"/",
|
||||||
async ({ params, body, request, set }) => {
|
async ({ params, body, request, set }) => {
|
||||||
const guard = await requireRole(request.headers, [
|
const guard = await requireRole(request.headers, [
|
||||||
@@ -120,36 +113,29 @@ export const presetSettingsRoutes = new Elysia({
|
|||||||
return { error: "Not authorized to modify this preset" }
|
return { error: "Not authorized to modify this preset" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete existing settings and re-insert
|
const [updated] = await db
|
||||||
await db
|
.update(communityPresets)
|
||||||
.delete(presetSettings)
|
.set({
|
||||||
.where(eq(presetSettings.presetId, params.presetId))
|
settingsJson: body.settings,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(communityPresets.id, params.presetId))
|
||||||
|
.returning()
|
||||||
|
|
||||||
if (body.settings.length > 0) {
|
return updated
|
||||||
await db.insert(presetSettings).values(
|
|
||||||
body.settings.map((s: { settingDefinitionId: string; value: boolean | string | number }) => ({
|
|
||||||
presetId: params.presetId,
|
|
||||||
settingDefinitionId: s.settingDefinitionId,
|
|
||||||
value: s.value,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the updated settings
|
|
||||||
const data = await db
|
|
||||||
.select()
|
|
||||||
.from(presetSettings)
|
|
||||||
.where(eq(presetSettings.presetId, params.presetId))
|
|
||||||
|
|
||||||
return data
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
params: t.Object({ presetId: t.String() }),
|
params: t.Object({ presetId: t.String() }),
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
settings: t.Array(
|
settings: t.Array(
|
||||||
t.Object({
|
t.Object({
|
||||||
settingDefinitionId: t.String(),
|
category: t.String(),
|
||||||
value: t.Union([t.Boolean(), t.String(), t.Number()]),
|
settings: t.Array(
|
||||||
|
t.Object({
|
||||||
|
title: t.String(),
|
||||||
|
value: t.Union([t.String(), t.Number(), t.Boolean()]),
|
||||||
|
}),
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import { Elysia } from "elysia"
|
|
||||||
import { createCrudRoutes } from "./crud-builder"
|
|
||||||
import { settingCategories, settingDefinitions } from "@/lib/db/schema"
|
|
||||||
import { db } from "@/lib/db/index"
|
|
||||||
import { asc } from "drizzle-orm"
|
|
||||||
|
|
||||||
// ── Setting Categories CRUD ───────────────────────────────────────
|
|
||||||
export const settingCategoriesRoutes = createCrudRoutes(settingCategories, {
|
|
||||||
prefix: "/settings/categories",
|
|
||||||
name: "Setting Category",
|
|
||||||
auth: { read: "public", write: "admin", delete: "admin" },
|
|
||||||
})
|
|
||||||
|
|
||||||
// ── Setting Definitions CRUD ──────────────────────────────────────
|
|
||||||
export const settingDefinitionsRoutes = createCrudRoutes(settingDefinitions, {
|
|
||||||
prefix: "/settings/definitions",
|
|
||||||
name: "Setting Definition",
|
|
||||||
auth: { read: "public", write: "admin", delete: "admin" },
|
|
||||||
filter: { fields: ["categoryId", "inputType", "impactLevel"] },
|
|
||||||
})
|
|
||||||
|
|
||||||
// ── Combined endpoint: categories with their definitions ──────────
|
|
||||||
// This is the main endpoint the frontend uses to render dynamic forms.
|
|
||||||
export const settingsFullRoutes = new Elysia({
|
|
||||||
prefix: "/settings/full",
|
|
||||||
}).get(
|
|
||||||
"/",
|
|
||||||
async () => {
|
|
||||||
const categories = await db
|
|
||||||
.select()
|
|
||||||
.from(settingCategories)
|
|
||||||
.orderBy(asc(settingCategories.sortOrder))
|
|
||||||
|
|
||||||
const definitions = await db
|
|
||||||
.select()
|
|
||||||
.from(settingDefinitions)
|
|
||||||
.orderBy(asc(settingDefinitions.sortOrder))
|
|
||||||
|
|
||||||
// Group definitions by category
|
|
||||||
const result = categories.map((cat) => ({
|
|
||||||
...cat,
|
|
||||||
definitions: definitions.filter((d) => d.categoryId === cat.id),
|
|
||||||
}))
|
|
||||||
|
|
||||||
return result
|
|
||||||
},
|
|
||||||
)
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
integer,
|
integer,
|
||||||
|
jsonb,
|
||||||
pgTable,
|
pgTable,
|
||||||
text,
|
text,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -27,6 +28,13 @@ export const communityPresets = pgTable(
|
|||||||
onDelete: "set null",
|
onDelete: "set null",
|
||||||
}),
|
}),
|
||||||
upvotes: integer("upvotes").default(0).notNull(),
|
upvotes: integer("upvotes").default(0).notNull(),
|
||||||
|
// Freeform settings JSON — same flexible structure as performanceEntries.settingsJson
|
||||||
|
settingsJson: jsonb("settings_json").$type<
|
||||||
|
{
|
||||||
|
category: string
|
||||||
|
settings: { title: string; value: string | number | boolean }[]
|
||||||
|
}[]
|
||||||
|
>(),
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,8 +5,5 @@ export * from "./gameVersions"
|
|||||||
export * from "./hardware"
|
export * from "./hardware"
|
||||||
export * from "./performanceEntries"
|
export * from "./performanceEntries"
|
||||||
export * from "./gamePlatformSupport"
|
export * from "./gamePlatformSupport"
|
||||||
export * from "./settingCategories"
|
|
||||||
export * from "./settingDefinitions"
|
|
||||||
export * from "./communityPresets"
|
export * from "./communityPresets"
|
||||||
export * from "./presetSettings"
|
|
||||||
export * from "./gameComments"
|
export * from "./gameComments"
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import { jsonb, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core"
|
|
||||||
import { communityPresets } from "./communityPresets"
|
|
||||||
import { settingDefinitions } from "./settingDefinitions"
|
|
||||||
|
|
||||||
export const presetSettings = pgTable(
|
|
||||||
"preset_settings",
|
|
||||||
{
|
|
||||||
id: text("id")
|
|
||||||
.primaryKey()
|
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
|
||||||
presetId: text("preset_id")
|
|
||||||
.notNull()
|
|
||||||
.references(() => communityPresets.id, { onDelete: "cascade" }),
|
|
||||||
settingDefinitionId: text("setting_definition_id")
|
|
||||||
.notNull()
|
|
||||||
.references(() => settingDefinitions.id, { onDelete: "cascade" }),
|
|
||||||
// Value stored as JSON — boolean for toggle, string for select,
|
|
||||||
// number for range/number
|
|
||||||
value: jsonb("value").notNull().$type<boolean | string | number>(),
|
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
||||||
},
|
|
||||||
(table) => [
|
|
||||||
unique("preset_setting_unique").on(table.presetId, table.settingDefinitionId),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core"
|
|
||||||
|
|
||||||
export const settingCategories = pgTable("setting_categories", {
|
|
||||||
id: text("id")
|
|
||||||
.primaryKey()
|
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
|
||||||
name: text("name").notNull(),
|
|
||||||
slug: text("slug").notNull().unique(),
|
|
||||||
sortOrder: integer("sort_order").default(0).notNull(),
|
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
||||||
})
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import {
|
|
||||||
integer,
|
|
||||||
jsonb,
|
|
||||||
pgEnum,
|
|
||||||
pgTable,
|
|
||||||
text,
|
|
||||||
timestamp,
|
|
||||||
unique,
|
|
||||||
} from "drizzle-orm/pg-core"
|
|
||||||
import { settingCategories } from "./settingCategories"
|
|
||||||
|
|
||||||
export const settingInputTypeEnum = pgEnum("setting_input_type", [
|
|
||||||
"toggle",
|
|
||||||
"select",
|
|
||||||
"range",
|
|
||||||
"number",
|
|
||||||
])
|
|
||||||
|
|
||||||
export const impactLevelEnum = pgEnum("impact_level", [
|
|
||||||
"minor",
|
|
||||||
"moderate",
|
|
||||||
"major",
|
|
||||||
])
|
|
||||||
|
|
||||||
export const settingDefinitions = pgTable(
|
|
||||||
"setting_definitions",
|
|
||||||
{
|
|
||||||
id: text("id")
|
|
||||||
.primaryKey()
|
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
|
||||||
categoryId: text("category_id")
|
|
||||||
.notNull()
|
|
||||||
.references(() => settingCategories.id, { onDelete: "cascade" }),
|
|
||||||
name: text("name").notNull(),
|
|
||||||
slug: text("slug").notNull(),
|
|
||||||
inputType: settingInputTypeEnum("input_type").notNull(),
|
|
||||||
// For `select`: string[] of option labels
|
|
||||||
// For `range`: { min: number, max: number, step: number }
|
|
||||||
// For `toggle`/`number`: null
|
|
||||||
options: jsonb("options").$type<string[] | { min: number; max: number; step: number }>(),
|
|
||||||
impactLevel: impactLevelEnum("impact_level").default("minor").notNull(),
|
|
||||||
sortOrder: integer("sort_order").default(0).notNull(),
|
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
||||||
},
|
|
||||||
(table) => [unique("setting_category_slug_unique").on(table.categoryId, table.slug)],
|
|
||||||
)
|
|
||||||
-210
@@ -1,10 +1,7 @@
|
|||||||
import "dotenv/config"
|
import "dotenv/config"
|
||||||
import { drizzle } from "drizzle-orm/node-postgres"
|
import { drizzle } from "drizzle-orm/node-postgres"
|
||||||
import { Pool } from "pg"
|
import { Pool } from "pg"
|
||||||
import { eq } from "drizzle-orm"
|
|
||||||
import { hardware } from "./schema/hardware"
|
import { hardware } from "./schema/hardware"
|
||||||
import { settingCategories } from "./schema/settingCategories"
|
|
||||||
import { settingDefinitions } from "./schema/settingDefinitions"
|
|
||||||
|
|
||||||
const pool = new Pool({
|
const pool = new Pool({
|
||||||
connectionString: process.env.DATABASE_URL,
|
connectionString: process.env.DATABASE_URL,
|
||||||
@@ -45,213 +42,6 @@ async function seed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Seeded ${devices.length} hardware devices.`)
|
console.log(`Seeded ${devices.length} hardware devices.`)
|
||||||
|
|
||||||
// ── Setting Categories ────────────────────────────────────────────
|
|
||||||
console.log("Seeding setting categories...")
|
|
||||||
|
|
||||||
const categories = [
|
|
||||||
{ name: "Graphics", slug: "graphics", sortOrder: 0 },
|
|
||||||
{ name: "Display", slug: "display", sortOrder: 1 },
|
|
||||||
{ name: "Audio", slug: "audio", sortOrder: 2 },
|
|
||||||
{ name: "Controls", slug: "controls", sortOrder: 3 },
|
|
||||||
{ name: "Gameplay", slug: "gameplay", sortOrder: 4 },
|
|
||||||
]
|
|
||||||
|
|
||||||
const insertedCategories: Record<string, string> = {} // slug → id
|
|
||||||
|
|
||||||
for (const cat of categories) {
|
|
||||||
const [inserted] = await db
|
|
||||||
.insert(settingCategories)
|
|
||||||
.values(cat)
|
|
||||||
.onConflictDoNothing({ target: settingCategories.slug })
|
|
||||||
.returning()
|
|
||||||
|
|
||||||
if (inserted) {
|
|
||||||
insertedCategories[cat.slug] = inserted.id
|
|
||||||
} else {
|
|
||||||
// Fetch existing
|
|
||||||
const existing = await db
|
|
||||||
.select()
|
|
||||||
.from(settingCategories)
|
|
||||||
.where(eq(settingCategories.slug, cat.slug))
|
|
||||||
.limit(1)
|
|
||||||
if (existing[0]) {
|
|
||||||
insertedCategories[cat.slug] = existing[0].id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Seeded ${categories.length} setting categories.`)
|
|
||||||
|
|
||||||
// ── Setting Definitions ──────────────────────────────────────────
|
|
||||||
console.log("Seeding setting definitions...")
|
|
||||||
|
|
||||||
const definitions = [
|
|
||||||
// Graphics
|
|
||||||
{
|
|
||||||
categorySlug: "graphics",
|
|
||||||
name: "Resolution",
|
|
||||||
slug: "resolution",
|
|
||||||
inputType: "select" as const,
|
|
||||||
options: ["720p", "800p", "1080p", "1200p", "1440p", "4K"],
|
|
||||||
impactLevel: "major" as const,
|
|
||||||
sortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "graphics",
|
|
||||||
name: "Texture Quality",
|
|
||||||
slug: "texture-quality",
|
|
||||||
inputType: "select" as const,
|
|
||||||
options: ["Low", "Medium", "High", "Ultra"],
|
|
||||||
impactLevel: "major" as const,
|
|
||||||
sortOrder: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "graphics",
|
|
||||||
name: "Shadow Quality",
|
|
||||||
slug: "shadow-quality",
|
|
||||||
inputType: "select" as const,
|
|
||||||
options: ["Off", "Low", "Medium", "High", "Ultra"],
|
|
||||||
impactLevel: "major" as const,
|
|
||||||
sortOrder: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "graphics",
|
|
||||||
name: "Anti-Aliasing",
|
|
||||||
slug: "anti-aliasing",
|
|
||||||
inputType: "select" as const,
|
|
||||||
options: ["Off", "FXAA", "TAA", "MSAA 2x", "MSAA 4x"],
|
|
||||||
impactLevel: "moderate" as const,
|
|
||||||
sortOrder: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "graphics",
|
|
||||||
name: "Volumetric Fog",
|
|
||||||
slug: "volumetric-fog",
|
|
||||||
inputType: "toggle" as const,
|
|
||||||
options: null,
|
|
||||||
impactLevel: "moderate" as const,
|
|
||||||
sortOrder: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "graphics",
|
|
||||||
name: "Motion Blur",
|
|
||||||
slug: "motion-blur",
|
|
||||||
inputType: "toggle" as const,
|
|
||||||
options: null,
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 5,
|
|
||||||
},
|
|
||||||
// Display
|
|
||||||
{
|
|
||||||
categorySlug: "display",
|
|
||||||
name: "Refresh Rate",
|
|
||||||
slug: "refresh-rate",
|
|
||||||
inputType: "select" as const,
|
|
||||||
options: ["30Hz", "40Hz", "60Hz", "90Hz", "120Hz"],
|
|
||||||
impactLevel: "moderate" as const,
|
|
||||||
sortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "display",
|
|
||||||
name: "V-Sync",
|
|
||||||
slug: "v-sync",
|
|
||||||
inputType: "toggle" as const,
|
|
||||||
options: null,
|
|
||||||
impactLevel: "moderate" as const,
|
|
||||||
sortOrder: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "display",
|
|
||||||
name: "Frame Rate Limit",
|
|
||||||
slug: "frame-rate-limit",
|
|
||||||
inputType: "range" as const,
|
|
||||||
options: { min: 15, max: 120, step: 5 },
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 2,
|
|
||||||
},
|
|
||||||
// Audio
|
|
||||||
{
|
|
||||||
categorySlug: "audio",
|
|
||||||
name: "Master Volume",
|
|
||||||
slug: "master-volume",
|
|
||||||
inputType: "range" as const,
|
|
||||||
options: { min: 0, max: 100, step: 5 },
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "audio",
|
|
||||||
name: "SFX Volume",
|
|
||||||
slug: "sfx-volume",
|
|
||||||
inputType: "range" as const,
|
|
||||||
options: { min: 0, max: 100, step: 5 },
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "audio",
|
|
||||||
name: "Music Volume",
|
|
||||||
slug: "music-volume",
|
|
||||||
inputType: "range" as const,
|
|
||||||
options: { min: 0, max: 100, step: 5 },
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 2,
|
|
||||||
},
|
|
||||||
// Controls
|
|
||||||
{
|
|
||||||
categorySlug: "controls",
|
|
||||||
name: "Controller Vibration",
|
|
||||||
slug: "controller-vibration",
|
|
||||||
inputType: "toggle" as const,
|
|
||||||
options: null,
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
categorySlug: "controls",
|
|
||||||
name: "Aim Sensitivity",
|
|
||||||
slug: "aim-sensitivity",
|
|
||||||
inputType: "range" as const,
|
|
||||||
options: { min: 1, max: 10, step: 1 },
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 1,
|
|
||||||
},
|
|
||||||
// Gameplay
|
|
||||||
{
|
|
||||||
categorySlug: "gameplay",
|
|
||||||
name: "Difficulty",
|
|
||||||
slug: "difficulty",
|
|
||||||
inputType: "select" as const,
|
|
||||||
options: ["Easy", "Normal", "Hard", "Extreme"],
|
|
||||||
impactLevel: "minor" as const,
|
|
||||||
sortOrder: 0,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
let definitionCount = 0
|
|
||||||
for (const def of definitions) {
|
|
||||||
const categoryId = insertedCategories[def.categorySlug]
|
|
||||||
if (!categoryId) continue
|
|
||||||
|
|
||||||
await db
|
|
||||||
.insert(settingDefinitions)
|
|
||||||
.values({
|
|
||||||
categoryId,
|
|
||||||
name: def.name,
|
|
||||||
slug: def.slug,
|
|
||||||
inputType: def.inputType,
|
|
||||||
options: def.options,
|
|
||||||
impactLevel: def.impactLevel,
|
|
||||||
sortOrder: def.sortOrder,
|
|
||||||
})
|
|
||||||
.onConflictDoNothing({ target: settingDefinitions.slug })
|
|
||||||
|
|
||||||
definitionCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Seeded ${definitionCount} setting definitions.`)
|
|
||||||
|
|
||||||
await pool.end()
|
await pool.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user