Creating new landing page, starting a different flow of managing your games, updating the schema for external ids, and adding different logo.

This commit is contained in:
Bradley Shellnut 2024-02-14 17:48:47 -08:00
parent ec5f1ed93b
commit 2b3d037861
19 changed files with 1634 additions and 394 deletions

View file

@ -0,0 +1,71 @@
CREATE TABLE IF NOT EXISTS "categories_to_external_ids" (
"category_id" varchar(255) NOT NULL,
"external_id" varchar(255) NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "expansions_to_external_ids" (
"expansion_id" varchar(255) NOT NULL,
"external_id" varchar(255) NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "mechanics_to_external_ids" (
"mechanic_id" varchar(255) NOT NULL,
"external_id" varchar(255) NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "publishers_to_external_ids" (
"publisher_id" varchar(255) NOT NULL,
"external_id" varchar(255) NOT NULL
);
--> statement-breakpoint
DROP TABLE "artists";--> statement-breakpoint
DROP TABLE "artists_to_games";--> statement-breakpoint
DROP TABLE "designers";--> statement-breakpoint
DROP TABLE "designers_to_games";--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "categories_to_external_ids" ADD CONSTRAINT "categories_to_external_ids_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "categories"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "categories_to_external_ids" ADD CONSTRAINT "categories_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "external_ids"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "expansions_to_external_ids" ADD CONSTRAINT "expansions_to_external_ids_expansion_id_expansions_id_fk" FOREIGN KEY ("expansion_id") REFERENCES "expansions"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "expansions_to_external_ids" ADD CONSTRAINT "expansions_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "external_ids"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "mechanics_to_external_ids" ADD CONSTRAINT "mechanics_to_external_ids_mechanic_id_mechanics_id_fk" FOREIGN KEY ("mechanic_id") REFERENCES "mechanics"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "mechanics_to_external_ids" ADD CONSTRAINT "mechanics_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "external_ids"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "publishers_to_external_ids" ADD CONSTRAINT "publishers_to_external_ids_publisher_id_publishers_id_fk" FOREIGN KEY ("publisher_id") REFERENCES "publishers"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "publishers_to_external_ids" ADD CONSTRAINT "publishers_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "external_ids"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,13 @@
"when": 1707932522909,
"tag": "0006_light_corsair",
"breakpoints": true
},
{
"idx": 7,
"version": "5",
"when": 1707951501716,
"tag": "0007_same_valeria_richards",
"breakpoints": true
}
]
}

View file

@ -18,7 +18,7 @@
"site:update": "pnpm update -i -L",
"generate": "drizzle-kit generate:pg",
"migrate": "tsx ./src/migrate.ts",
"seed": "tsx ./src/seed/insert.ts",
"seed": "tsx ./src/seed.ts",
"push": "drizzle-kit push:pg"
},
"prisma": {
@ -35,6 +35,7 @@
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/cookie": "^0.6.0",
"@types/node": "^20.11.17",
"@types/pg": "^8.11.0",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"autoprefixer": "^10.4.17",

View file

@ -64,7 +64,7 @@ dependencies:
version: 0.6.0
drizzle-orm:
specifier: ^0.29.3
version: 0.29.3(@neondatabase/serverless@0.8.1)(@planetscale/database@1.16.0)(mysql2@3.9.1)(pg@8.11.3)(postgres@3.4.3)
version: 0.29.3(@neondatabase/serverless@0.8.1)(@planetscale/database@1.16.0)(@types/pg@8.11.0)(mysql2@3.9.1)(pg@8.11.3)(postgres@3.4.3)
feather-icons:
specifier: ^4.29.1
version: 4.29.1
@ -160,6 +160,9 @@ devDependencies:
'@types/node':
specifier: ^20.11.17
version: 20.11.17
'@types/pg':
specifier: ^8.11.0
version: 8.11.0
'@typescript-eslint/eslint-plugin':
specifier: ^6.21.0
version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
@ -3071,6 +3074,13 @@ packages:
dependencies:
undici-types: 5.26.5
/@types/pg@8.11.0:
resolution: {integrity: sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==}
dependencies:
'@types/node': 20.11.17
pg-protocol: 1.6.0
pg-types: 4.0.2
/@types/pg@8.6.6:
resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==}
dependencies:
@ -3989,7 +3999,7 @@ packages:
- supports-color
dev: true
/drizzle-orm@0.29.3(@neondatabase/serverless@0.8.1)(@planetscale/database@1.16.0)(mysql2@3.9.1)(pg@8.11.3)(postgres@3.4.3):
/drizzle-orm@0.29.3(@neondatabase/serverless@0.8.1)(@planetscale/database@1.16.0)(@types/pg@8.11.0)(mysql2@3.9.1)(pg@8.11.3)(postgres@3.4.3):
resolution: {integrity: sha512-uSE027csliGSGYD0pqtM+SAQATMREb3eSM/U8s6r+Y0RFwTKwftnwwSkqx3oS65UBgqDOM0gMTl5UGNpt6lW0A==}
peerDependencies:
'@aws-sdk/client-rds-data': '>=3'
@ -4062,6 +4072,7 @@ packages:
dependencies:
'@neondatabase/serverless': 0.8.1
'@planetscale/database': 1.16.0
'@types/pg': 8.11.0
mysql2: 3.9.1
pg: 8.11.3
postgres: 3.4.3
@ -5490,6 +5501,9 @@ packages:
object-keys: 1.1.1
dev: false
/obuf@1.1.2:
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
/once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
@ -5634,7 +5648,10 @@ packages:
/pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
dev: false
/pg-numeric@1.0.2:
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
engines: {node: '>=4'}
/pg-pool@3.6.1(pg@8.11.3):
resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==}
@ -5646,7 +5663,6 @@ packages:
/pg-protocol@1.6.0:
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
dev: false
/pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
@ -5659,6 +5675,18 @@ packages:
postgres-interval: 1.2.0
dev: false
/pg-types@4.0.2:
resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==}
engines: {node: '>=10'}
dependencies:
pg-int8: 1.0.1
pg-numeric: 1.0.2
postgres-array: 3.0.2
postgres-bytea: 3.0.0
postgres-date: 2.1.0
postgres-interval: 3.0.0
postgres-range: 1.1.4
/pg@8.11.3:
resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==}
engines: {node: '>= 8.0.0'}
@ -6184,16 +6212,30 @@ packages:
engines: {node: '>=4'}
dev: false
/postgres-array@3.0.2:
resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
engines: {node: '>=12'}
/postgres-bytea@1.0.0:
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-bytea@3.0.0:
resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
engines: {node: '>= 6'}
dependencies:
obuf: 1.1.2
/postgres-date@1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-date@2.1.0:
resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==}
engines: {node: '>=12'}
/postgres-interval@1.2.0:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
@ -6201,6 +6243,13 @@ packages:
xtend: 4.0.2
dev: false
/postgres-interval@3.0.0:
resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
engines: {node: '>=12'}
/postgres-range@1.1.4:
resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==}
/postgres@3.4.3:
resolution: {integrity: sha512-iHJn4+M9vbTdHSdDzNkC0crHq+1CUdFhx+YqCE+SqWxPjm+Zu63jq7yZborOBF64c8pc58O5uMudyL1FQcHacA==}
engines: {node: '>=12'}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB

View file

@ -1,12 +1,9 @@
<script>
import { PUBLIC_SITE_URL } from "$env/static/public";
</script>
<footer>
<p>Built by <a target="__blank" href="https://bradleyshellnut.com">Bradley Shellnut</a></p>
<p>
<a
target="__blank"
href="https://www.flaticon.com/free-icons/board-game"
title="board game icons">Board game icons created by Freepik - Flaticon</a
>
</p>
<p>Bored Game &copy; {new Date().getFullYear()} | Built by <a target="__blank" href="https://bradleyshellnut.com">Bradley Shellnut</a> | {PUBLIC_SITE_URL}</p>
</footer>
<style lang="postcss">

View file

@ -15,7 +15,7 @@
<header>
<div class="corner">
<a href="/" class="logo" title="Home">
<div class="logo-image">
<div class="logo">
<Logo />
</div>
</a>
@ -126,10 +126,10 @@
height: 100%;
}
.logo-image {
width: 2rem;
height: 2rem;
object-fit: contain;
.logo {
width: 2.5rem;
height: 2.5rem;
overflow: hidden;
}
nav {

View file

@ -1,5 +1,13 @@
<script lang="ts">
import logo from '$lib/assets/bored-game.png';
</script>
<img src={logo} alt="Bored Game Home" />
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-go-game" viewBox="0 0 24 24" stroke-width="1" stroke="var(--fg)" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M6 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M12 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M6 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M18 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
<path d="M3 12h7m4 0h7" />
<path d="M3 6h1m4 0h13" />
<path d="M3 18h1m4 0h8m4 0h1" />
<path d="M6 3v1m0 4v8m0 4v1" />
<path d="M12 3v7m0 4v7" />
<path d="M18 3v13m0 4v1" />
</svg>

Before

Width:  |  Height:  |  Size: 118 B

After

Width:  |  Height:  |  Size: 668 B

View file

@ -1,6 +1,6 @@
import { drizzle } from "drizzle-orm/node-postgres";
import pg from 'pg';
import { DATABASE_USER, DATABASE_PASSWORD, DATABASE_HOST, DATABASE_DB } from '$env/static/private';
import { DATABASE_USER, DATABASE_PASSWORD, DATABASE_HOST, DATABASE_DB, DATABASE_PORT } from '$env/static/private';
import * as schema from '../schema';
// create the connection
@ -8,10 +8,10 @@ const pool = new pg.Pool({
user: DATABASE_USER,
password: DATABASE_PASSWORD,
host: DATABASE_HOST,
port: 3306,
database: DATABASE_DB
port: new Number(DATABASE_PORT).valueOf(),
database: DATABASE_DB,
});
const db = drizzle(pool, { schema: schema });
const db = drizzle(pool, { schema });
export default db;

View file

@ -26,6 +26,7 @@
--input: 20 5.9% 90%;
--ring: 20 14.3% 4.1%;
--radius: 0.5rem;
--fg: #2c3e50;
}
.dark {
--background: 20 14.3% 4.1%;
@ -47,6 +48,7 @@
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 35.5 91.7% 32.9%;
--fg: #ffff;
}
}

View file

@ -8,10 +8,18 @@ const connection = postgres({
port: 3306,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_DB
database: process.env.DATABASE_DB,
ssl: 'require',
max: 1
});
const db = drizzle(connection);
await migrate(db, { migrationsFolder: 'drizzle' });
try {
await migrate(db, { migrationsFolder: 'drizzle' });
console.log('Migrations complete');
} catch (e) {
console.error(e);
}
await connection.end();
process.exit();

View file

@ -37,14 +37,5 @@ export const load: PageServerLoad = async ({ fetch, url }) => {
}
});
const formData = Object.fromEntries(url?.searchParams);
console.log('formData', formData);
formData.name = formData?.q;
const form = await superValidate(formData, search_schema);
console.log('form', form);
const randomGames: Game[] = await fetch('/api/game/random?limit=6').then(res => res.json());
console.log('randomGames', randomGames);
return { form, metaTagsChild: metaTags, randomGames };
return { metaTagsChild: metaTags };
};

View file

@ -1,63 +1,26 @@
<script lang="ts">
// import TextSearch from '$lib/components/search/textSearch/header.svelte';
import RandomSearch from '$lib/components/search/random/index.svelte';
import Game from '$components/Game.svelte';
import logo from '$lib/assets/bored-game.png';
// import Random from '$lib/components/random/header.svelte';
export let data;
const { randomGames } = data;
import Logo from "$components/logo.svelte";
</script>
<h1>Search Boardgames!</h1>
<div class="game-search">
<section>
<p style="margin: 1rem 0;">
Input your requirements to search for board games that match your criteria.
</p>
<p>Or pick a random game!</p>
<div class="random-buttons">
<RandomSearch data={data.form} />
<!-- <Random /> -->
</div>
</section>
<!-- <TextSearch showButton advancedSearch data={data.form} /> -->
<div class="container">
<div class="logo">
<Logo />
</div>
<h1>Welcome to Bored Game</h1>
<h2>Keep track of your board games!</h2>
<p>The ones you own, the ones you want, and whether you play them enough.</p>
<p>Get started by joinging the <a href="/waitlist">waitlist</a> or <a href="/login">signing in</a>.</p>
</div>
<section class="random-games">
{#each randomGames as game (game.id)}
<Game variant='compact' {game} />
{/each}
</section>
<style lang="postcss">
.game-search {
.container {
max-width: 900px;
display: grid;
gap: 2rem;
section {
display: grid;
gap: 1rem;
}
gap: 0.25rem;
}
.random-games {
margin-top: 1rem;
display: grid;
gap: 0.5rem;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}
.random-buttons {
display: flex;
place-content: space-between;
place-items: center;
@media (max-width: 650px) {
display: grid;
grid-template-columns: 1fr 1fr;
}
.logo {
width: 4rem;
height: 4rem;
}
</style>

View file

@ -0,0 +1 @@
<h1>Waitlist</h1>

View file

@ -1,10 +1,9 @@
// import prisma from '$lib/prisma.js';
import db from '$lib/drizzle.js';
import { error, json } from '@sveltejs/kit';
import { asc, count, inArray, sql } from 'drizzle-orm';
import { asc, count } from 'drizzle-orm';
import { games, type Games } from '../../../../schema.js';
export const GET = async ({ url, locals }) => {
export const GET = async ({ url }) => {
const searchParams = Object.fromEntries(url.searchParams);
const limit = parseInt(searchParams?.limit) || 1;

View file

@ -3,6 +3,8 @@ import { pgTable, timestamp, varchar, boolean, integer, text, index, pgEnum } fr
import { nanoid } from 'nanoid';
import { tsvector } from './tsVector';
// User Related Schemas
export const users = pgTable('users', {
id: varchar('id', {
length: 255
@ -71,6 +73,12 @@ export const roles = pgTable('roles', {
}).unique()
});
export type Roles = InferSelectModel<typeof roles>;
export const role_relations = relations(roles, ({ many }) => ({
user_roles: many(user_roles)
}))
export const user_roles = pgTable('user_roles', {
id: varchar('id', {
length: 255
@ -110,138 +118,6 @@ export const user_role_relations = relations(user_roles, ({ one }) => ({
})
}));
export const games = pgTable('games', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
name: varchar('name', {
length: 255
}),
slug: varchar('slug', {
length: 255
}),
description: text('description'),
year_published: integer('year_published'),
min_players: integer('min_players'),
max_players: integer('max_players'),
playtime: integer('playtime'),
min_playtime: integer('min_playtime'),
max_playtime: integer('max_playtime'),
min_age: integer('min_age'),
image_url: varchar('image_url', {
length: 255
}),
thumb_url: varchar('thumb_url', {
length: 255
}),
url: varchar('url', {
length: 255
}),
text_searchable_index: tsvector('text_searchable_index'),
last_sync_at: timestamp('last_sync_at', {
withTimezone: true,
mode: 'date',
precision: 6
}),
created_at: timestamp('created_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`),
updated_at: timestamp('updated_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`)
}, (table) => {
return {
text_searchable_idx: index("text_searchable_idx").on(table.text_searchable_index).using(sql`'gin'`)
}
});
export type Games = InferSelectModel<typeof games>;
export const gameRelations = relations(games, ({ many }) => ({
categories_to_games: many(categories_to_games),
mechanics_to_games: many(mechanics_to_games),
designers_to_games: many(designers_to_games),
publishers_to_games: many(publishers_to_games),
artists_to_games: many(artists_to_games),
gameExternalIds: many(gameExternalIds),
}));
export const externalIdType = pgEnum('external_id_type', [
'game', 'category', 'mechanic', 'publisher', 'designer', 'artist'
]);
export const externalIds = pgTable('external_ids', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
type: varchar('type', {
length: 255
}).notNull(),
externalId: varchar('external_id', {
length: 255
}).notNull()
});
export const gamesToExternalIds = pgTable('games_to_external_ids', {
gameId: varchar('game_id', {
length: 255
})
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
externalId: varchar('external_id', {
length: 255
})
.notNull()
.references(() => externalIds.id, { onDelete: 'cascade' }),
});
export const expansions = pgTable('expansions', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
base_game_id: varchar('base_game_id', {
length: 255
})
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
game_id: varchar('game_id', {
length: 255
})
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
created_at: timestamp('created_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`),
updated_at: timestamp('updated_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`)
});
export const expansion_relations = relations(expansions, ({ one }) => ({
baseGame: one(games, {
fields: [expansions.base_game_id],
references: [games.id]
}),
game: one(games, {
fields: [expansions.game_id],
references: [games.id]
})
}));
export const collections = pgTable('collections', {
id: varchar('id', {
length: 255
@ -380,6 +256,152 @@ export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
})
}));
// Game and related table schemas
export const externalIdType = pgEnum('external_id_type', [
'game', 'category', 'mechanic', 'publisher', 'designer', 'artist'
]);
export const externalIds = pgTable('external_ids', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
type: varchar('type', {
length: 255
}).notNull(),
externalId: varchar('external_id', {
length: 255
}).notNull()
});
export const games = pgTable('games', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
name: varchar('name', {
length: 255
}),
slug: varchar('slug', {
length: 255
}),
description: text('description'),
year_published: integer('year_published'),
min_players: integer('min_players'),
max_players: integer('max_players'),
playtime: integer('playtime'),
min_playtime: integer('min_playtime'),
max_playtime: integer('max_playtime'),
min_age: integer('min_age'),
image_url: varchar('image_url', {
length: 255
}),
thumb_url: varchar('thumb_url', {
length: 255
}),
url: varchar('url', {
length: 255
}),
text_searchable_index: tsvector('text_searchable_index'),
last_sync_at: timestamp('last_sync_at', {
withTimezone: true,
mode: 'date',
precision: 6
}),
created_at: timestamp('created_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`),
updated_at: timestamp('updated_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`)
}, (table) => {
return {
text_searchable_idx: index("text_searchable_idx").on(table.text_searchable_index).using(sql`'gin'`)
}
});
export type Games = InferSelectModel<typeof games>;
export const gamesToExternalIds = pgTable('games_to_external_ids', {
gameId: varchar('game_id', {
length: 255
})
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
externalId: varchar('external_id', {
length: 255
})
.notNull()
.references(() => externalIds.id, { onDelete: 'cascade' }),
});
export const gameRelations = relations(games, ({ many }) => ({
categories_to_games: many(categories_to_games),
mechanics_to_games: many(mechanics_to_games),
publishers_to_games: many(publishers_to_games),
gamesToExternalIds: many(gamesToExternalIds),
}));
export const expansions = pgTable('expansions', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
base_game_id: varchar('base_game_id', {
length: 255
})
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
game_id: varchar('game_id', {
length: 255
})
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
created_at: timestamp('created_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`),
updated_at: timestamp('updated_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`)
});
export const expansionsToExternalIds = pgTable('expansions_to_external_ids', {
expansionId: varchar('expansion_id', {
length: 255
})
.notNull()
.references(() => expansions.id, { onDelete: 'cascade' }),
externalId: varchar('external_id', {
length: 255
})
.notNull()
.references(() => externalIds.id, { onDelete: 'cascade' }),
});
export const expansion_relations = relations(expansions, ({ one, many }) => ({
baseGame: one(games, {
fields: [expansions.base_game_id],
references: [games.id]
}),
game: one(games, {
fields: [expansions.game_id],
references: [games.id]
}),
expansionsToExternalIds: many(expansionsToExternalIds)
}));
export const publishers = pgTable('publishers', {
id: varchar('id', {
length: 255
@ -405,8 +427,22 @@ export const publishers = pgTable('publishers', {
}).default(sql`now()`)
});
export const publishersToExternalIds = pgTable('publishers_to_external_ids', {
publisherId: varchar('publisher_id', {
length: 255
})
.notNull()
.references(() => publishers.id, { onDelete: 'cascade' }),
externalId: varchar('external_id', {
length: 255
})
.notNull()
.references(() => externalIds.id, { onDelete: 'cascade' }),
});
export const publishers_relations = relations(publishers, ({ many }) => ({
publishers_to_games: many(publishers_to_games)
publishers_to_games: many(publishers_to_games),
publishersToExternalIds: many(publishersToExternalIds)
}));
export const categories = pgTable('categories', {
@ -434,8 +470,42 @@ export const categories = pgTable('categories', {
}).default(sql`now()`)
});
export const categoriesToExternalIds = pgTable('categories_to_external_ids', {
categoryId: varchar('category_id', {
length: 255
})
.notNull()
.references(() => categories.id, { onDelete: 'cascade' }),
externalId: varchar('external_id', {
length: 255
})
.notNull()
.references(() => externalIds.id, { onDelete: 'cascade' }),
})
export const categories_to_games = pgTable('categories_to_games', {
category_id: varchar('category_id', {
length: 255
}),
game_id: varchar('game_id', {
length: 255
})
});
export const categories_to_games_relations = relations(categories_to_games, ({ one }) => ({
category: one(categories, {
fields: [categories_to_games.category_id],
references: [categories.id]
}),
game: one(games, {
fields: [categories_to_games.game_id],
references: [games.id]
})
}));
export const categories_relations = relations(categories, ({ many }) => ({
categories_to_games: many(categories_to_games)
categories_to_games: many(categories_to_games),
categoriesToExternalIds: many(categoriesToExternalIds)
}));
export const mechanics = pgTable('mechanics', {
@ -463,126 +533,22 @@ export const mechanics = pgTable('mechanics', {
}).default(sql`now()`)
});
export const mechanicsToExternalIds = pgTable('mechanics_to_external_ids', {
mechanicId: varchar('mechanic_id', {
length: 255
})
.notNull()
.references(() => mechanics.id, { onDelete: 'cascade' }),
externalId: varchar('external_id', {
length: 255
})
.notNull()
.references(() => externalIds.id, { onDelete: 'cascade' }),
})
export const mechanic_relations = relations(mechanics, ({ many }) => ({
mechanics_to_games: many(mechanics_to_games)
}));
export const designers = pgTable('designers', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
name: varchar('name', {
length: 255
}),
slug: varchar('slug', {
length: 255
}),
external_id: integer('external_id'),
created_at: timestamp('created_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`),
updated_at: timestamp('updated_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`)
});
export const designers_relations = relations(designers, ({ many }) => ({
designers_to_games: many(designers_to_games)
}));
export const artists = pgTable('artists', {
id: varchar('id', {
length: 255
})
.primaryKey()
.$defaultFn(() => nanoid()),
name: varchar('name', {
length: 255
}),
slug: varchar('slug', {
length: 255
}),
external_id: integer('external_id'),
created_at: timestamp('created_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`),
updated_at: timestamp('updated_at', {
withTimezone: true,
mode: 'date',
precision: 6
}).default(sql`now()`)
});
export const artists_relations = relations(artists, ({ many }) => ({
artists_to_games: many(artists_to_games)
}));
export const artists_to_games = pgTable('artists_to_games', {
artist_id: varchar('artist_id', {
length: 255
}),
game_id: varchar('game_id', {
length: 255
})
});
export const artists_to_games_relations = relations(artists_to_games, ({ one }) => ({
artist: one(artists, {
fields: [artists_to_games.artist_id],
references: [artists.id]
}),
game: one(games, {
fields: [artists_to_games.game_id],
references: [games.id]
})
}));
export const categories_to_games = pgTable('categories_to_games', {
category_id: varchar('category_id', {
length: 255
}),
game_id: varchar('game_id', {
length: 255
})
});
export const categories_to_games_relations = relations(categories_to_games, ({ one }) => ({
category: one(categories, {
fields: [categories_to_games.category_id],
references: [categories.id]
}),
game: one(games, {
fields: [categories_to_games.game_id],
references: [games.id]
})
}));
export const designers_to_games = pgTable('designers_to_games', {
designer_id: varchar('designer_id', {
length: 255
}),
game_id: varchar('game_id', {
length: 255
})
});
export const designers_to_games_relations = relations(designers_to_games, ({ one }) => ({
designer: one(designers, {
fields: [designers_to_games.designer_id],
references: [designers.id]
}),
game: one(games, {
fields: [designers_to_games.game_id],
references: [games.id]
})
mechanics_to_games: many(mechanics_to_games),
mechanicsToExternalIds: many(mechanicsToExternalIds)
}));
export const mechanics_to_games = pgTable('mechanics_to_games', {

29
src/seed.ts Normal file
View file

@ -0,0 +1,29 @@
import 'dotenv/config';
import { drizzle } from "drizzle-orm/node-postgres";
import pg from 'pg';
import * as schema from './schema';
// create the connection
const pool = new pg.Pool({
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
host: process.env.DATABASE_HOST,
port: new Number(process.env.DATABASE_PORT).valueOf(),
database: process.env.DATABASE_DB
});
const db = drizzle(pool, { schema: schema });
const existingRoles = await db.query.roles.findMany();
console.log('Existing roles', existingRoles);
if (existingRoles.length === 0) {
console.log('Creating roles ...');
await db.insert(schema.roles).values([{
name: 'admin'
}, {
name: 'user'
}]);
console.log('Roles created.');
} else {
console.log('Roles already exist. No action taken.');
}

View file

@ -1,47 +0,0 @@
import 'dotenv/config';
import { drizzle } from "drizzle-orm/node-postgres";
import { sql } from 'drizzle-orm';
import pg from 'pg';
import * as schema from '../schema';
// create the connection
const pool = new pg.Pool({
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
database: process.env.DATABASE_DB
});
const db = drizzle(pool, { schema: schema });
const existingRoles = await db.query.roles.findMany();
if (existingRoles.length === 0) {
console.log('Creating roles ...');
await db.insert(schema.roles).values([{
name: 'admin'
}, {
name: 'user'
}]);
console.log('Roles created.');
} else {
console.log('Roles already exist. No action taken.');
}
const indexes = await db.execute(sql`select * from pg_catalog.pg_indexes where tablename = 'games'`);
console.log('Indexes', indexes);
const nameSlugIndexExists = indexes[0].flatMap((i) => i.Key_name).indexOf('full_text_name_slug_index') > -1;
console.log('nameSlugIndexExists', nameSlugIndexExists);
if (!nameSlugIndexExists) {
console.log('Full Text Index does not exist. Creating...');
// Create index
await db.execute(sql`alter table games ADD FULLTEXT INDEX full_text_name_slug_index (name, slug)`);
} else {
console.log('Full Text Index already exists. No action taken.');
}
await connection.end();