mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Moving drizzle into it's own DB folder and moving all the table types to separate files.
This commit is contained in:
parent
0b58b3ff8f
commit
a8eed0d86d
98 changed files with 5648 additions and 6076 deletions
|
|
@ -2,8 +2,8 @@ import 'dotenv/config';
|
||||||
import { defineConfig } from 'drizzle-kit';
|
import { defineConfig } from 'drizzle-kit';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
schema: './src/schema.ts',
|
schema: './src/db/schema/index.ts',
|
||||||
out: './drizzle',
|
out: './src/db/migrations',
|
||||||
driver: 'pg',
|
driver: 'pg',
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
host: process.env.DATABASE_HOST || 'localhost',
|
host: process.env.DATABASE_HOST || 'localhost',
|
||||||
|
|
@ -11,10 +11,10 @@ export default defineConfig({
|
||||||
user: process.env.DATABASE_USER,
|
user: process.env.DATABASE_USER,
|
||||||
password: process.env.DATABASE_PASSWORD,
|
password: process.env.DATABASE_PASSWORD,
|
||||||
database: process.env.DATABASE || 'boredgame',
|
database: process.env.DATABASE || 'boredgame',
|
||||||
ssl: process.env.DATABASE_HOST !== 'localhost'
|
ssl: process.env.DATABASE_HOST !== 'localhost',
|
||||||
},
|
},
|
||||||
// Print all statements
|
// Print all statements
|
||||||
verbose: true,
|
verbose: true,
|
||||||
// Always as for confirmation
|
// Always as for confirmation
|
||||||
strict: true
|
strict: true,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
"format": "prettier --plugin-search-dir . --write .",
|
"format": "prettier --plugin-search-dir . --write .",
|
||||||
"site:update": "pnpm update -i -L",
|
"site:update": "pnpm update -i -L",
|
||||||
"generate": "drizzle-kit generate:pg",
|
"generate": "drizzle-kit generate:pg",
|
||||||
"migrate": "tsx ./src/migrate.ts",
|
"migrate": "tsx src/db/migrate.ts",
|
||||||
"seed": "tsx ./src/seed.ts",
|
"seed": "tsx src/db/seed.ts",
|
||||||
"push": "drizzle-kit push:pg"
|
"push": "drizzle-kit push:pg"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
9298
pnpm-lock.yaml
9298
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -5,9 +5,11 @@ import {
|
||||||
DATABASE_PASSWORD,
|
DATABASE_PASSWORD,
|
||||||
DATABASE_HOST,
|
DATABASE_HOST,
|
||||||
DATABASE_DB,
|
DATABASE_DB,
|
||||||
DATABASE_PORT
|
DATABASE_PORT,
|
||||||
|
DB_MIGRATING,
|
||||||
|
DB_SEEDING,
|
||||||
} from '$env/static/private';
|
} from '$env/static/private';
|
||||||
import * as schema from '../schema';
|
import * as schema from './schema';
|
||||||
|
|
||||||
// create the connection
|
// create the connection
|
||||||
const pool = new pg.Pool({
|
const pool = new pg.Pool({
|
||||||
|
|
@ -16,15 +18,15 @@ const pool = new pg.Pool({
|
||||||
host: DATABASE_HOST,
|
host: DATABASE_HOST,
|
||||||
port: Number(DATABASE_PORT).valueOf(),
|
port: Number(DATABASE_PORT).valueOf(),
|
||||||
database: DATABASE_DB,
|
database: DATABASE_DB,
|
||||||
ssl: DATABASE_HOST !== 'localhost'
|
ssl: DATABASE_HOST !== 'localhost',
|
||||||
|
max: DB_MIGRATING || DB_SEEDING ? 1 : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
// user: DATABASE_USER,
|
const db = drizzle(pool, {
|
||||||
// password: DATABASE_PASSWORD,
|
schema,
|
||||||
// host: DATABASE_HOST,
|
logger: process.env.NODE_ENV === 'development',
|
||||||
// port: Number(DATABASE_PORT).valueOf(),
|
});
|
||||||
// database: DATABASE_DB
|
|
||||||
|
|
||||||
const db = drizzle(pool, { schema });
|
export type db = typeof db;
|
||||||
|
|
||||||
export default db;
|
export default db;
|
||||||
24
src/db/schema/categories.ts
Normal file
24
src/db/schema/categories.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import categories_to_games from './categoriesToGames';
|
||||||
|
|
||||||
|
const categories = pgTable('categories', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
name: text('name'),
|
||||||
|
slug: text('slug'),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Categories = InferSelectModel<typeof categories>;
|
||||||
|
|
||||||
|
export const categories_relations = relations(categories, ({ many }) => ({
|
||||||
|
categories_to_games: many(categories_to_games),
|
||||||
|
categoriesToExternalIds: many(categoriesToExternalIds),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default categories;
|
||||||
24
src/db/schema/categoriesToExternalIds.ts
Normal file
24
src/db/schema/categoriesToExternalIds.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import categories from './categories';
|
||||||
|
import externalIds from './externalIds';
|
||||||
|
|
||||||
|
const categoriesToExternalIds = pgTable(
|
||||||
|
'categories_to_external_ids',
|
||||||
|
{
|
||||||
|
categoryId: uuid('category_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
externalId: uuid('external_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
categoriesToExternalIdsPkey: primaryKey({
|
||||||
|
columns: [table.categoryId, table.externalId],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default categoriesToExternalIds;
|
||||||
36
src/db/schema/categoriesToGames.ts
Normal file
36
src/db/schema/categoriesToGames.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { relations } from 'drizzle-orm';
|
||||||
|
import categories from './categories';
|
||||||
|
import games from './games';
|
||||||
|
|
||||||
|
const categories_to_games = pgTable(
|
||||||
|
'categories_to_games',
|
||||||
|
{
|
||||||
|
category_id: uuid('category_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
game_id: uuid('game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
categoriesToGamesPkey: primaryKey({
|
||||||
|
columns: [table.category_id, table.game_id],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
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 default categories_to_games;
|
||||||
36
src/db/schema/collectionItems.ts
Normal file
36
src/db/schema/collectionItems.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import collections from './collections';
|
||||||
|
import games from './games';
|
||||||
|
|
||||||
|
const collection_items = pgTable('collection_items', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
collection_id: uuid('collection_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => collections.id, { onDelete: 'cascade' }),
|
||||||
|
game_id: uuid('game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'cascade' }),
|
||||||
|
times_played: integer('times_played').default(0),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CollectionItems = InferSelectModel<typeof collection_items>;
|
||||||
|
|
||||||
|
export const collection_item_relations = relations(collection_items, ({ one }) => ({
|
||||||
|
collection: one(collections, {
|
||||||
|
fields: [collection_items.collection_id],
|
||||||
|
references: [collections.id],
|
||||||
|
}),
|
||||||
|
game: one(games, {
|
||||||
|
fields: [collection_items.game_id],
|
||||||
|
references: [games.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default collection_items;
|
||||||
28
src/db/schema/collections.ts
Normal file
28
src/db/schema/collections.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
|
const collections = pgTable('collections', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
user_id: uuid('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' }),
|
||||||
|
name: text('name').notNull().default('My Collection'),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collection_relations = relations(collections, ({ one }) => ({
|
||||||
|
user: one(users, {
|
||||||
|
fields: [collections.user_id],
|
||||||
|
references: [users.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export type Collections = InferSelectModel<typeof collections>;
|
||||||
|
|
||||||
|
export default collections;
|
||||||
32
src/db/schema/expansions.ts
Normal file
32
src/db/schema/expansions.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import games from './games';
|
||||||
|
|
||||||
|
export const expansions = pgTable('expansions', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
base_game_id: uuid('base_game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
game_id: uuid('game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Expansions = InferSelectModel<typeof expansions>;
|
||||||
|
|
||||||
|
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],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
25
src/db/schema/externalIds.ts
Normal file
25
src/db/schema/externalIds.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { pgEnum, pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import type { InferSelectModel } from 'drizzle-orm';
|
||||||
|
|
||||||
|
export const ExternalIdType = pgEnum('external_id_type', [
|
||||||
|
'game',
|
||||||
|
'category',
|
||||||
|
'mechanic',
|
||||||
|
'publisher',
|
||||||
|
'designer',
|
||||||
|
'artist',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const externalIds = pgTable('external_ids', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
type: ExternalIdType('type').notNull(),
|
||||||
|
externalId: text('external_id').notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ExternalIds = InferSelectModel<typeof externalIds>;
|
||||||
|
|
||||||
|
export default externalIds;
|
||||||
57
src/db/schema/games.ts
Normal file
57
src/db/schema/games.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { index, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { tsvector } from '../../tsVector';
|
||||||
|
import { type InferSelectModel, relations, sql } from 'drizzle-orm';
|
||||||
|
import categoriesToGames from './categoriesToGames';
|
||||||
|
import gamesToExternalIds from './gamesToExternalIds';
|
||||||
|
import mechanicsToGames from './mechanicsToGames';
|
||||||
|
import publishersToGames from './publishersToGames';
|
||||||
|
|
||||||
|
const games = pgTable(
|
||||||
|
'games',
|
||||||
|
{
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
name: text('name'),
|
||||||
|
slug: text('slug'),
|
||||||
|
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: text('image_url'),
|
||||||
|
thumb_url: text('thumb_url'),
|
||||||
|
url: text('url'),
|
||||||
|
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').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
text_searchable_idx: index('text_searchable_idx')
|
||||||
|
.on(table.text_searchable_index)
|
||||||
|
.using(sql`'gin'`),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const gameRelations = relations(games, ({ many }) => ({
|
||||||
|
categories_to_games: many(categoriesToGames),
|
||||||
|
mechanics_to_games: many(mechanicsToGames),
|
||||||
|
publishers_to_games: many(publishersToGames),
|
||||||
|
gamesToExternalIds: many(gamesToExternalIds),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export type Games = InferSelectModel<typeof games>;
|
||||||
|
|
||||||
|
export default games;
|
||||||
24
src/db/schema/gamesToExternalIds.ts
Normal file
24
src/db/schema/gamesToExternalIds.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import games from './games';
|
||||||
|
import externalIds from './externalIds';
|
||||||
|
|
||||||
|
const gamesToExternalIds = pgTable(
|
||||||
|
'games_to_external_ids',
|
||||||
|
{
|
||||||
|
gameId: uuid('game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
externalId: uuid('external_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
gamesToExternalIdsPkey: primaryKey({
|
||||||
|
columns: [table.gameId, table.externalId],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default gamesToExternalIds;
|
||||||
34
src/db/schema/index.ts
Normal file
34
src/db/schema/index.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
export { default as users, user_relations, type Users } from './users';
|
||||||
|
export { default as recoveryCodes, type RecoveryCodes } from './recoveryCodes';
|
||||||
|
export {
|
||||||
|
default as password_reset_tokens,
|
||||||
|
password_reset_token_relations,
|
||||||
|
type PasswordResetTokens,
|
||||||
|
} from './passwordResetTokens';
|
||||||
|
export { default as sessions } from './sessions';
|
||||||
|
export { default as roles, role_relations, type Roles } from './roles';
|
||||||
|
export { default as userRoles, user_role_relations, type UserRoles } from './userRoles';
|
||||||
|
export { default as collections, collection_relations, type Collections } from './collections';
|
||||||
|
export {
|
||||||
|
default as collectionItems,
|
||||||
|
collection_item_relations,
|
||||||
|
type CollectionItems,
|
||||||
|
} from './collectionItems';
|
||||||
|
export { default as wishlists, wishlists_relations, type Wishlists } from './wishlists';
|
||||||
|
export {
|
||||||
|
default as wishlistItems,
|
||||||
|
wishlist_item_relations,
|
||||||
|
type WishlistItems,
|
||||||
|
} from './wishlistItems';
|
||||||
|
export { default as externalIds, type ExternalIds, type ExternalIdType } from './externalIds';
|
||||||
|
export { default as games, gameRelations, type Games } from './games';
|
||||||
|
export { default as gamesToExternalIds } from './gamesToExternalIds';
|
||||||
|
export { default as publishers, publishers_relations, type Publishers } from './publishers';
|
||||||
|
export { default as publishersToGames, publishers_to_games_relations } from './publishersToGames';
|
||||||
|
export { default as publishersToExternalIds } from './publishersToExternalIds';
|
||||||
|
export { default as categories, categories_relations, type Categories } from './categories';
|
||||||
|
export { default as categoriesToExternalIds } from './categoriesToExternalIds';
|
||||||
|
export { default as categoriesToGames, categories_to_games_relations } from './categoriesToGames';
|
||||||
|
export { default as mechanics, mechanics_relations, type Mechanics } from './mechanics';
|
||||||
|
export { default as mechanicsToExternalIds } from './mechanicsToExternalIds';
|
||||||
|
export { default as mechanicsToGames, mechanics_to_games_relations } from './mechanicsToGames';
|
||||||
25
src/db/schema/mechanics.ts
Normal file
25
src/db/schema/mechanics.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import mechanicsToGames from './mechanicsToGames';
|
||||||
|
import mechanicsToExternalIds from './mechanicsToExternalIds';
|
||||||
|
|
||||||
|
const mechanics = pgTable('mechanics', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
name: text('name'),
|
||||||
|
slug: text('slug'),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Mechanics = InferSelectModel<typeof mechanics>;
|
||||||
|
|
||||||
|
export const mechanics_relations = relations(mechanics, ({ many }) => ({
|
||||||
|
mechanics_to_games: many(mechanicsToGames),
|
||||||
|
mechanicsToExternalIds: many(mechanicsToExternalIds),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default mechanics;
|
||||||
24
src/db/schema/mechanicsToExternalIds.ts
Normal file
24
src/db/schema/mechanicsToExternalIds.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import mechanics from './mechanics';
|
||||||
|
import externalIds from './externalIds';
|
||||||
|
|
||||||
|
const mechanicsToExternalIds = pgTable(
|
||||||
|
'mechanics_to_external_ids',
|
||||||
|
{
|
||||||
|
mechanicId: uuid('mechanic_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
externalId: uuid('external_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
mechanicsToExternalIdsPkey: primaryKey({
|
||||||
|
columns: [table.mechanicId, table.externalId],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default mechanicsToExternalIds;
|
||||||
36
src/db/schema/mechanicsToGames.ts
Normal file
36
src/db/schema/mechanicsToGames.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { relations } from 'drizzle-orm';
|
||||||
|
import mechanics from './mechanics';
|
||||||
|
import games from './games';
|
||||||
|
|
||||||
|
const mechanics_to_games = pgTable(
|
||||||
|
'mechanics_to_games',
|
||||||
|
{
|
||||||
|
mechanic_id: uuid('mechanic_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
game_id: uuid('game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
mechanicsToGamesPkey: primaryKey({
|
||||||
|
columns: [table.mechanic_id, table.game_id],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const mechanics_to_games_relations = relations(mechanics_to_games, ({ one }) => ({
|
||||||
|
mechanic: one(mechanics, {
|
||||||
|
fields: [mechanics_to_games.mechanic_id],
|
||||||
|
references: [mechanics.id],
|
||||||
|
}),
|
||||||
|
game: one(games, {
|
||||||
|
fields: [mechanics_to_games.game_id],
|
||||||
|
references: [games.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default mechanics_to_games;
|
||||||
30
src/db/schema/passwordResetTokens.ts
Normal file
30
src/db/schema/passwordResetTokens.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
|
const password_reset_tokens = pgTable('password_reset_tokens', {
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
user_id: uuid('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' }),
|
||||||
|
expires_at: timestamp('expires_at', {
|
||||||
|
withTimezone: true,
|
||||||
|
mode: 'date',
|
||||||
|
precision: 6,
|
||||||
|
}),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PasswordResetTokens = InferSelectModel<typeof password_reset_tokens>;
|
||||||
|
|
||||||
|
export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({
|
||||||
|
user: one(users, {
|
||||||
|
fields: [password_reset_tokens.user_id],
|
||||||
|
references: [users.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default password_reset_tokens;
|
||||||
25
src/db/schema/publishers.ts
Normal file
25
src/db/schema/publishers.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import publishers_to_games from './publishersToGames';
|
||||||
|
import publishersToExternalIds from './publishersToExternalIds';
|
||||||
|
|
||||||
|
const publishers = pgTable('publishers', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
name: text('name'),
|
||||||
|
slug: text('slug'),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Publishers = InferSelectModel<typeof publishers>;
|
||||||
|
|
||||||
|
export const publishers_relations = relations(publishers, ({ many }) => ({
|
||||||
|
publishers_to_games: many(publishers_to_games),
|
||||||
|
publishersToExternalIds: many(publishersToExternalIds),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default publishers;
|
||||||
24
src/db/schema/publishersToExternalIds.ts
Normal file
24
src/db/schema/publishersToExternalIds.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import publishers from './publishers';
|
||||||
|
import externalIds from './externalIds';
|
||||||
|
|
||||||
|
const publishersToExternalIds = pgTable(
|
||||||
|
'publishers_to_external_ids',
|
||||||
|
{
|
||||||
|
publisherId: uuid('publisher_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
externalId: uuid('external_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
publishersToExternalIdsPkey: primaryKey({
|
||||||
|
columns: [table.publisherId, table.externalId],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default publishersToExternalIds;
|
||||||
36
src/db/schema/publishersToGames.ts
Normal file
36
src/db/schema/publishersToGames.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { relations } from 'drizzle-orm';
|
||||||
|
import publishers from './publishers';
|
||||||
|
import games from './games';
|
||||||
|
|
||||||
|
const publishers_to_games = pgTable(
|
||||||
|
'publishers_to_games',
|
||||||
|
{
|
||||||
|
publisher_id: uuid('publisher_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
game_id: uuid('game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
|
},
|
||||||
|
(table) => {
|
||||||
|
return {
|
||||||
|
publishersToGamesPkey: primaryKey({
|
||||||
|
columns: [table.publisher_id, table.game_id],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({
|
||||||
|
publisher: one(publishers, {
|
||||||
|
fields: [publishers_to_games.publisher_id],
|
||||||
|
references: [publishers.id],
|
||||||
|
}),
|
||||||
|
game: one(games, {
|
||||||
|
fields: [publishers_to_games.game_id],
|
||||||
|
references: [games.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default publishers_to_games;
|
||||||
18
src/db/schema/recoveryCodes.ts
Normal file
18
src/db/schema/recoveryCodes.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import type { InferSelectModel } from 'drizzle-orm';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
|
const recovery_codes = pgTable('recovery_codes', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
userId: uuid('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
code: text('code').notNull(),
|
||||||
|
used: boolean('used').default(false),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RecoveryCodes = InferSelectModel<typeof recovery_codes>;
|
||||||
|
|
||||||
|
export default recovery_codes;
|
||||||
21
src/db/schema/roles.ts
Normal file
21
src/db/schema/roles.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import user_roles from './userRoles';
|
||||||
|
|
||||||
|
const roles = pgTable('roles', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2())
|
||||||
|
.notNull(),
|
||||||
|
name: text('name').unique().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Roles = InferSelectModel<typeof roles>;
|
||||||
|
|
||||||
|
export const role_relations = relations(roles, ({ many }) => ({
|
||||||
|
user_roles: many(user_roles),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default roles;
|
||||||
17
src/db/schema/sessions.ts
Normal file
17
src/db/schema/sessions.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
|
const sessions = pgTable('sessions', {
|
||||||
|
id: text('id').primaryKey(),
|
||||||
|
userId: uuid('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
expiresAt: timestamp('expires_at', {
|
||||||
|
withTimezone: true,
|
||||||
|
mode: 'date',
|
||||||
|
}).notNull(),
|
||||||
|
ipCountry: text('ip_country'),
|
||||||
|
ipAddress: text('ip_address'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default sessions;
|
||||||
36
src/db/schema/userRoles.ts
Normal file
36
src/db/schema/userRoles.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import users from './users';
|
||||||
|
import roles from './roles';
|
||||||
|
|
||||||
|
const user_roles = pgTable('user_roles', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
user_id: uuid('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' }),
|
||||||
|
role_id: uuid('role_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => roles.id, { onDelete: 'cascade' }),
|
||||||
|
primary: boolean('primary').default(false),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const user_role_relations = relations(user_roles, ({ one }) => ({
|
||||||
|
role: one(roles, {
|
||||||
|
fields: [user_roles.role_id],
|
||||||
|
references: [roles.id],
|
||||||
|
}),
|
||||||
|
user: one(users, {
|
||||||
|
fields: [user_roles.user_id],
|
||||||
|
references: [users.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export type UserRoles = InferSelectModel<typeof user_roles>;
|
||||||
|
|
||||||
|
export default user_roles;
|
||||||
31
src/db/schema/users.ts
Normal file
31
src/db/schema/users.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import user_roles from './userRoles';
|
||||||
|
|
||||||
|
const users = pgTable('users', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
username: text('username').unique(),
|
||||||
|
hashed_password: text('hashed_password'),
|
||||||
|
email: text('email').unique(),
|
||||||
|
first_name: text('first_name'),
|
||||||
|
last_name: text('last_name'),
|
||||||
|
verified: boolean('verified').default(false),
|
||||||
|
receive_email: boolean('receive_email').default(false),
|
||||||
|
theme: text('theme').default('system'),
|
||||||
|
two_factor_secret: text('two_factor_secret').default(''),
|
||||||
|
two_factor_enabled: boolean('two_factor_enabled').default(false),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const user_relations = relations(users, ({ many }) => ({
|
||||||
|
user_roles: many(user_roles),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export type Users = InferSelectModel<typeof users>;
|
||||||
|
|
||||||
|
export default users;
|
||||||
35
src/db/schema/wishlistItems.ts
Normal file
35
src/db/schema/wishlistItems.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import wishlists from './wishlists';
|
||||||
|
import games from './games';
|
||||||
|
|
||||||
|
const wishlist_items = pgTable('wishlist_items', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
wishlist_id: uuid('wishlist_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => wishlists.id, { onDelete: 'cascade' }),
|
||||||
|
game_id: uuid('game_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => games.id, { onDelete: 'cascade' }),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type WishlistItems = InferSelectModel<typeof wishlist_items>;
|
||||||
|
|
||||||
|
export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
|
||||||
|
wishlist: one(wishlists, {
|
||||||
|
fields: [wishlist_items.wishlist_id],
|
||||||
|
references: [wishlists.id],
|
||||||
|
}),
|
||||||
|
game: one(games, {
|
||||||
|
fields: [wishlist_items.game_id],
|
||||||
|
references: [games.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default wishlist_items;
|
||||||
28
src/db/schema/wishlists.ts
Normal file
28
src/db/schema/wishlists.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
|
const wishlists = pgTable('wishlists', {
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
cuid: text('cuid')
|
||||||
|
.unique()
|
||||||
|
.$defaultFn(() => cuid2()),
|
||||||
|
user_id: uuid('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' }),
|
||||||
|
name: text('name').notNull().default('My Wishlist'),
|
||||||
|
created_at: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Wishlists = InferSelectModel<typeof wishlists>;
|
||||||
|
|
||||||
|
export const wishlists_relations = relations(wishlists, ({ one }) => ({
|
||||||
|
user: one(users, {
|
||||||
|
fields: [wishlists.user_id],
|
||||||
|
references: [users.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default wishlists;
|
||||||
|
|
@ -2,7 +2,7 @@ import 'dotenv/config';
|
||||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
import pg from 'pg';
|
import pg from 'pg';
|
||||||
import * as schema from './schema';
|
import * as schema from './schema';
|
||||||
import {Argon2id} from "oslo/password";
|
import { Argon2id } from 'oslo/password';
|
||||||
|
|
||||||
// create the connection
|
// create the connection
|
||||||
const pool = new pg.Pool({
|
const pool = new pg.Pool({
|
||||||
|
|
@ -11,7 +11,7 @@ const pool = new pg.Pool({
|
||||||
host: process.env.DATABASE_HOST,
|
host: process.env.DATABASE_HOST,
|
||||||
port: Number(process.env.DATABASE_PORT).valueOf(),
|
port: Number(process.env.DATABASE_PORT).valueOf(),
|
||||||
database: process.env.DATABASE_DB,
|
database: process.env.DATABASE_DB,
|
||||||
ssl: process.env.DATABASE_HOST !== 'localhost'
|
ssl: process.env.DATABASE_HOST !== 'localhost',
|
||||||
});
|
});
|
||||||
|
|
||||||
const db = drizzle(pool, { schema: schema });
|
const db = drizzle(pool, { schema: schema });
|
||||||
|
|
@ -40,31 +40,37 @@ console.log('Roles created.');
|
||||||
console.log('Admin Role: ', adminRole);
|
console.log('Admin Role: ', adminRole);
|
||||||
|
|
||||||
const adminUser = await db
|
const adminUser = await db
|
||||||
.insert(schema.users)
|
.insert(schema.users)
|
||||||
.values({
|
.values({
|
||||||
username: `${process.env.ADMIN_USERNAME}`,
|
username: `${process.env.ADMIN_USERNAME}`,
|
||||||
email: '',
|
email: '',
|
||||||
hashed_password: await new Argon2id().hash(`${process.env.ADMIN_PASSWORD}`),
|
hashed_password: await new Argon2id().hash(`${process.env.ADMIN_PASSWORD}`),
|
||||||
first_name: 'Brad',
|
first_name: 'Brad',
|
||||||
last_name: 'S',
|
last_name: 'S',
|
||||||
verified: true
|
verified: true,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
.onConflictDoNothing();
|
.onConflictDoNothing();
|
||||||
|
|
||||||
console.log('Admin user created.', adminUser);
|
console.log('Admin user created.', adminUser);
|
||||||
|
|
||||||
await db.insert(schema.user_roles).values({
|
await db
|
||||||
user_id: adminUser[0].id,
|
.insert(schema.user_roles)
|
||||||
role_id: adminRole[0].id
|
.values({
|
||||||
}).onConflictDoNothing();
|
user_id: adminUser[0].id,
|
||||||
|
role_id: adminRole[0].id,
|
||||||
|
})
|
||||||
|
.onConflictDoNothing();
|
||||||
|
|
||||||
console.log('Admin user given admin role.');
|
console.log('Admin user given admin role.');
|
||||||
|
|
||||||
await db.insert(schema.user_roles).values({
|
await db
|
||||||
user_id: adminUser[0].id,
|
.insert(schema.user_roles)
|
||||||
role_id: userRole[0].id
|
.values({
|
||||||
}).onConflictDoNothing();
|
user_id: adminUser[0].id,
|
||||||
|
role_id: userRole[0].id,
|
||||||
|
})
|
||||||
|
.onConflictDoNothing();
|
||||||
|
|
||||||
console.log('Admin user given user role.');
|
console.log('Admin user given user role.');
|
||||||
|
|
||||||
44
src/env.ts
Normal file
44
src/env.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { ZodError, z } from 'zod';
|
||||||
|
|
||||||
|
const stringBoolean = z.coerce
|
||||||
|
.string()
|
||||||
|
.transform((val) => {
|
||||||
|
return val === 'true';
|
||||||
|
})
|
||||||
|
.default('false');
|
||||||
|
|
||||||
|
const EnvSchema = z.object({
|
||||||
|
NODE_ENV: z.string().default('development'),
|
||||||
|
DATABASE_USER: z.string(),
|
||||||
|
DATABASE_PASSWORD: z.string(),
|
||||||
|
DATABASE_HOST: z.string(),
|
||||||
|
DATABASE_PORT: z.coerce.number(),
|
||||||
|
DATABASE_DB: z.string(),
|
||||||
|
DATABASE_URL: z.string(),
|
||||||
|
PUBLIC_SITE_URL: z.string(),
|
||||||
|
PUBLIC_UMAMI_DO_NOT_TRACK: z.string(),
|
||||||
|
PUBLIC_UMAMI_ID: z.string(),
|
||||||
|
PUBLIC_UMAMI_URL: z.string(),
|
||||||
|
DB_MIGRATING: stringBoolean,
|
||||||
|
DB_SEEDING: stringBoolean,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type EnvSchema = z.infer<typeof EnvSchema>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
EnvSchema.parse(process.env);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ZodError) {
|
||||||
|
let message = 'Missing required values in .env:\n';
|
||||||
|
error.issues.forEach((issue) => {
|
||||||
|
message += issue.path[0] + '\n';
|
||||||
|
});
|
||||||
|
const e = new Error(message);
|
||||||
|
e.stack = '';
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EnvSchema.parse(process.env);
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { enhance } from "$app/forms";
|
import { enhance } from "$app/forms";
|
||||||
import { MinusCircle, PlusCircle } from "lucide-svelte";
|
import { MinusCircle, PlusCircle } from "lucide-svelte";
|
||||||
import { Button } from '$components/ui/button';
|
import { Button } from '$components/ui/button';
|
||||||
import type { CollectionItems, Wishlists } from "../../schema";
|
import type { CollectionItems, Wishlists } from "$db/schema";
|
||||||
|
|
||||||
export let game_id: string;
|
export let game_id: string;
|
||||||
export let collection: CollectionItems;
|
export let collection: CollectionItems;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { GameType, SavedGameType } from '$lib/types';
|
import type { GameType, SavedGameType } from '$lib/types';
|
||||||
import * as Card from "$lib/components/ui/card";
|
import * as Card from "$lib/components/ui/card";
|
||||||
import type { CollectionItems } from '../../schema';
|
import type { CollectionItems } from '$db/schema';
|
||||||
|
|
||||||
export let game: GameType | CollectionItems;
|
export let game: GameType | CollectionItems;
|
||||||
export let variant: 'default' | 'compact' = 'default';
|
export let variant: 'default' | 'compact' = 'default';
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
import * as Avatar from '$components/ui/avatar';
|
import * as Avatar from '$components/ui/avatar';
|
||||||
import { invalidateAll } from '$app/navigation';
|
import { invalidateAll } from '$app/navigation';
|
||||||
import Logo from '$components/logo.svelte';
|
import Logo from '$components/logo.svelte';
|
||||||
import type { Users } from '../../schema';
|
import type { Users } from '$db/schema';
|
||||||
|
|
||||||
export let user: Users | null = null;
|
export let user: Users | null = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { generateIdFromEntropySize } from 'lucia';
|
import { generateIdFromEntropySize } from 'lucia';
|
||||||
import { TimeSpan, createDate } from 'oslo';
|
import { TimeSpan, createDate } from 'oslo';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../db';
|
||||||
import { password_reset_tokens } from '../../schema';
|
import { password_reset_tokens } from '$db/schema';
|
||||||
|
|
||||||
export async function createPasswordResetToken(userId: string): Promise<string> {
|
export async function createPasswordResetToken(userId: string): Promise<string> {
|
||||||
// optionally invalidate all existing tokens
|
// optionally invalidate all existing tokens
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// lib/server/lucia.ts
|
// lib/server/lucia.ts
|
||||||
import { Lucia, TimeSpan } from 'lucia';
|
import { Lucia, TimeSpan } from 'lucia';
|
||||||
import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';
|
import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../db';
|
||||||
import { sessions, users } from '../../schema';
|
import { sessions, users } from '$db/schema';
|
||||||
|
|
||||||
const adapter = new DrizzlePostgreSQLAdapter(db, sessions, users);
|
const adapter = new DrizzlePostgreSQLAdapter(db, sessions, users);
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const lucia = new Lucia(adapter, {
|
||||||
getSessionAttributes: (attributes) => {
|
getSessionAttributes: (attributes) => {
|
||||||
return {
|
return {
|
||||||
ipCountry: attributes.ip_country,
|
ipCountry: attributes.ip_country,
|
||||||
ipAddress: attributes.ip_address
|
ipAddress: attributes.ip_address,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getUserAttributes: (attributes) => {
|
getUserAttributes: (attributes) => {
|
||||||
|
|
@ -28,7 +28,7 @@ export const lucia = new Lucia(adapter, {
|
||||||
email: attributes.email,
|
email: attributes.email,
|
||||||
firstName: attributes.firstName,
|
firstName: attributes.firstName,
|
||||||
lastName: attributes.lastName,
|
lastName: attributes.lastName,
|
||||||
theme: attributes.theme
|
theme: attributes.theme,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
sessionExpiresIn: new TimeSpan(30, 'd'), // 30 days
|
sessionExpiresIn: new TimeSpan(30, 'd'), // 30 days
|
||||||
|
|
@ -39,9 +39,9 @@ export const lucia = new Lucia(adapter, {
|
||||||
// set to `true` when using HTTPS
|
// set to `true` when using HTTPS
|
||||||
secure: process.env.NODE_ENV === 'production',
|
secure: process.env.NODE_ENV === 'production',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
domain
|
domain,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
declare module 'lucia' {
|
declare module 'lucia' {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { SvelteComponent } from 'svelte';
|
import type { SvelteComponent } from 'svelte';
|
||||||
import { collections } from '../schema';
|
import { collections } from '$db/schema';
|
||||||
|
|
||||||
export type Message = { status: 'error' | 'success' | 'warning' | 'info'; text: string };
|
export type Message = { status: 'error' | 'success' | 'warning' | 'info'; text: string };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,14 @@ import { error } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import kebabCase from 'just-kebab-case';
|
import kebabCase from 'just-kebab-case';
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../db';
|
||||||
import { externalIds, type Mechanics, type Categories, categories, categoriesToExternalIds } from '../../../schema';
|
import {
|
||||||
|
externalIds,
|
||||||
|
type Mechanics,
|
||||||
|
type Categories,
|
||||||
|
categories,
|
||||||
|
categoriesToExternalIds,
|
||||||
|
} from '$db/schema';
|
||||||
|
|
||||||
export async function createCategory(locals: App.Locals, category: Categories, externalId: string) {
|
export async function createCategory(locals: App.Locals, category: Categories, externalId: string) {
|
||||||
if (!category || !externalId || externalId === '') {
|
if (!category || !externalId || externalId === '') {
|
||||||
|
|
@ -12,7 +18,7 @@ export async function createCategory(locals: App.Locals, category: Categories, e
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbExternalId = await db.query.externalIds.findFirst({
|
const dbExternalId = await db.query.externalIds.findFirst({
|
||||||
where: eq(externalIds.externalId, externalId)
|
where: eq(externalIds.externalId, externalId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbExternalId) {
|
if (dbExternalId) {
|
||||||
|
|
@ -20,7 +26,7 @@ export async function createCategory(locals: App.Locals, category: Categories, e
|
||||||
.select({
|
.select({
|
||||||
id: categories.id,
|
id: categories.id,
|
||||||
name: categories.name,
|
name: categories.name,
|
||||||
slug: categories.slug
|
slug: categories.slug,
|
||||||
})
|
})
|
||||||
.from(categories)
|
.from(categories)
|
||||||
.leftJoin(categoriesToExternalIds, eq(categoriesToExternalIds.externalId, externalId));
|
.leftJoin(categoriesToExternalIds, eq(categoriesToExternalIds.externalId, externalId));
|
||||||
|
|
@ -30,9 +36,9 @@ export async function createCategory(locals: App.Locals, category: Categories, e
|
||||||
return new Response('Mechanic already exists', {
|
return new Response('Mechanic already exists', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Location: `${PUBLIC_SITE_URL}/api/mechanic/${foundCategory[0].id}`
|
Location: `${PUBLIC_SITE_URL}/api/mechanic/${foundCategory[0].id}`,
|
||||||
},
|
},
|
||||||
status: 409
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,25 +50,25 @@ export async function createCategory(locals: App.Locals, category: Categories, e
|
||||||
.insert(categories)
|
.insert(categories)
|
||||||
.values({
|
.values({
|
||||||
name: category.name,
|
name: category.name,
|
||||||
slug: kebabCase(category.name ?? category.slug ?? '')
|
slug: kebabCase(category.name ?? category.slug ?? ''),
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
const dbExternalIds = await transaction
|
const dbExternalIds = await transaction
|
||||||
.insert(externalIds)
|
.insert(externalIds)
|
||||||
.values({
|
.values({
|
||||||
externalId,
|
externalId,
|
||||||
type: 'category'
|
type: 'category',
|
||||||
})
|
})
|
||||||
.returning({ id: externalIds.id });
|
.returning({ id: externalIds.id });
|
||||||
await transaction.insert(categoriesToExternalIds).values({
|
await transaction.insert(categoriesToExternalIds).values({
|
||||||
categoryId: dbCategory[0].id,
|
categoryId: dbCategory[0].id,
|
||||||
externalId: dbExternalIds[0].id
|
externalId: dbExternalIds[0].id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbCategory.length === 0) {
|
if (dbCategory.length === 0) {
|
||||||
return new Response('Could not create category', {
|
return new Response('Could not create category', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../db';
|
||||||
import { type Expansions, expansions } from '../../../schema';
|
import { type Expansions, expansions } from '$db/schema';
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
|
|
||||||
export async function createExpansion(locals: App.Locals, expansion: Expansions) {
|
export async function createExpansion(locals: App.Locals, expansion: Expansions) {
|
||||||
|
|
@ -10,24 +10,26 @@ export async function createExpansion(locals: App.Locals, expansion: Expansions)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const foundExpansion = await db.query.expansions
|
const foundExpansion = await db.query.expansions.findFirst({
|
||||||
.findFirst({
|
where: and(
|
||||||
where: and(eq(expansions.base_game_id, expansion.base_game_id), eq(expansions.game_id, expansion.game_id)),
|
eq(expansions.base_game_id, expansion.base_game_id),
|
||||||
columns: {
|
eq(expansions.game_id, expansion.game_id),
|
||||||
id: true,
|
),
|
||||||
game_id: true,
|
columns: {
|
||||||
base_game_id: true
|
id: true,
|
||||||
}
|
game_id: true,
|
||||||
});
|
base_game_id: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
console.log('Expansion already exists', foundExpansion);
|
console.log('Expansion already exists', foundExpansion);
|
||||||
if (foundExpansion) {
|
if (foundExpansion) {
|
||||||
console.log('Expansion Game ID', foundExpansion.game_id);
|
console.log('Expansion Game ID', foundExpansion.game_id);
|
||||||
return new Response('Expansion already exists', {
|
return new Response('Expansion already exists', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Location: `${PUBLIC_SITE_URL}/api/game/${foundExpansion.game_id}`
|
Location: `${PUBLIC_SITE_URL}/api/game/${foundExpansion.game_id}`,
|
||||||
},
|
},
|
||||||
status: 409
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +44,7 @@ export async function createExpansion(locals: App.Locals, expansion: Expansions)
|
||||||
|
|
||||||
if (dbExpansion.length === 0) {
|
if (dbExpansion.length === 0) {
|
||||||
return new Response('Could not create expansion', {
|
return new Response('Could not create expansion', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import kebabCase from 'just-kebab-case';
|
import kebabCase from 'just-kebab-case';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../db';
|
||||||
import { externalIds, gamesToExternalIds, type Games, games } from '../../../schema';
|
import { externalIds, gamesToExternalIds, type Games, games } from '$db/schema';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
|
|
@ -12,12 +12,12 @@ export async function getGame(locals: App.Locals, id: string) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await db.query.games.findFirst({
|
return await db.query.games.findFirst({
|
||||||
where: eq(games.id, id)
|
where: eq(games.id, id),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return new Response('Could not get games', {
|
return new Response('Could not get games', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbExternalId = await db.query.externalIds.findFirst({
|
const dbExternalId = await db.query.externalIds.findFirst({
|
||||||
where: eq(externalIds.externalId, externalId)
|
where: eq(externalIds.externalId, externalId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbExternalId) {
|
if (dbExternalId) {
|
||||||
|
|
@ -37,7 +37,7 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
|
||||||
.select({
|
.select({
|
||||||
id: games.id,
|
id: games.id,
|
||||||
name: games.name,
|
name: games.name,
|
||||||
slug: games.slug
|
slug: games.slug,
|
||||||
})
|
})
|
||||||
.from(games)
|
.from(games)
|
||||||
.leftJoin(gamesToExternalIds, eq(gamesToExternalIds.externalId, externalId));
|
.leftJoin(gamesToExternalIds, eq(gamesToExternalIds.externalId, externalId));
|
||||||
|
|
@ -47,9 +47,9 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
|
||||||
return new Response('Game already exists', {
|
return new Response('Game already exists', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Location: `${PUBLIC_SITE_URL}/api/game/${foundGame[0].id}`
|
Location: `${PUBLIC_SITE_URL}/api/game/${foundGame[0].id}`,
|
||||||
},
|
},
|
||||||
status: 409
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,25 +71,25 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
|
||||||
min_players: game.min_players,
|
min_players: game.min_players,
|
||||||
max_players: game.max_players,
|
max_players: game.max_players,
|
||||||
min_playtime: game.min_playtime,
|
min_playtime: game.min_playtime,
|
||||||
max_playtime: game.max_playtime
|
max_playtime: game.max_playtime,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
const dbExternalIds = await transaction
|
const dbExternalIds = await transaction
|
||||||
.insert(externalIds)
|
.insert(externalIds)
|
||||||
.values({
|
.values({
|
||||||
externalId,
|
externalId,
|
||||||
type: 'game'
|
type: 'game',
|
||||||
})
|
})
|
||||||
.returning({ id: externalIds.id });
|
.returning({ id: externalIds.id });
|
||||||
await transaction.insert(gamesToExternalIds).values({
|
await transaction.insert(gamesToExternalIds).values({
|
||||||
gameId: dbGames[0].id,
|
gameId: dbGames[0].id,
|
||||||
externalId: dbExternalIds[0].id
|
externalId: dbExternalIds[0].id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbGames.length === 0) {
|
if (dbGames.length === 0) {
|
||||||
return new Response('Could not create game', {
|
return new Response('Could not create game', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +103,11 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games, externalId: string) {
|
export async function createOrUpdateGameMinimal(
|
||||||
|
locals: App.Locals,
|
||||||
|
game: Games,
|
||||||
|
externalId: string,
|
||||||
|
) {
|
||||||
if (!game || !externalId || externalId === '') {
|
if (!game || !externalId || externalId === '') {
|
||||||
error(400, 'Invalid Request');
|
error(400, 'Invalid Request');
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +132,7 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games,
|
||||||
min_players: game.min_players,
|
min_players: game.min_players,
|
||||||
max_players: game.max_players,
|
max_players: game.max_players,
|
||||||
min_playtime: game.min_playtime,
|
min_playtime: game.min_playtime,
|
||||||
max_playtime: game.max_playtime
|
max_playtime: game.max_playtime,
|
||||||
})
|
})
|
||||||
.onConflictDoUpdate({
|
.onConflictDoUpdate({
|
||||||
target: games.id,
|
target: games.id,
|
||||||
|
|
@ -144,27 +148,30 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games,
|
||||||
min_players: game.min_players,
|
min_players: game.min_players,
|
||||||
max_players: game.max_players,
|
max_players: game.max_players,
|
||||||
min_playtime: game.min_playtime,
|
min_playtime: game.min_playtime,
|
||||||
max_playtime: game.max_playtime
|
max_playtime: game.max_playtime,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
const dbExternalIds = await transaction
|
const dbExternalIds = await transaction
|
||||||
.insert(externalIds)
|
.insert(externalIds)
|
||||||
.values({
|
.values({
|
||||||
externalId,
|
externalId,
|
||||||
type: 'game'
|
type: 'game',
|
||||||
})
|
})
|
||||||
.onConflictDoNothing()
|
.onConflictDoNothing()
|
||||||
.returning({ id: externalIds.id });
|
.returning({ id: externalIds.id });
|
||||||
await transaction.insert(gamesToExternalIds).values({
|
await transaction
|
||||||
gameId: dbGames[0].id,
|
.insert(gamesToExternalIds)
|
||||||
externalId: dbExternalIds[0].id
|
.values({
|
||||||
}).onConflictDoNothing();
|
gameId: dbGames[0].id,
|
||||||
|
externalId: dbExternalIds[0].id,
|
||||||
|
})
|
||||||
|
.onConflictDoNothing();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbGames.length === 0) {
|
if (dbGames.length === 0) {
|
||||||
return new Response('Could not create game', {
|
return new Response('Could not create game', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +193,7 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
|
||||||
try {
|
try {
|
||||||
const externalUrl = `https://boardgamegeek.com/boardgame/${externalId}`;
|
const externalUrl = `https://boardgamegeek.com/boardgame/${externalId}`;
|
||||||
const dbExternalId = await db.query.externalIds.findFirst({
|
const dbExternalId = await db.query.externalIds.findFirst({
|
||||||
where: eq(externalIds.externalId, externalId)
|
where: eq(externalIds.externalId, externalId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbExternalId) {
|
if (dbExternalId) {
|
||||||
|
|
@ -194,7 +201,7 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
|
||||||
.select({
|
.select({
|
||||||
id: games.id,
|
id: games.id,
|
||||||
name: games.name,
|
name: games.name,
|
||||||
slug: games.slug
|
slug: games.slug,
|
||||||
})
|
})
|
||||||
.from(games)
|
.from(games)
|
||||||
.leftJoin(gamesToExternalIds, eq(gamesToExternalIds.externalId, externalId));
|
.leftJoin(gamesToExternalIds, eq(gamesToExternalIds.externalId, externalId));
|
||||||
|
|
@ -204,9 +211,9 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
|
||||||
return new Response('Game already exists', {
|
return new Response('Game already exists', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Location: `${PUBLIC_SITE_URL}/api/game/${foundGame[0].id}`
|
Location: `${PUBLIC_SITE_URL}/api/game/${foundGame[0].id}`,
|
||||||
},
|
},
|
||||||
status: 409
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +235,7 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
|
||||||
min_players: game.min_players,
|
min_players: game.min_players,
|
||||||
max_players: game.max_players,
|
max_players: game.max_players,
|
||||||
min_playtime: game.min_playtime,
|
min_playtime: game.min_playtime,
|
||||||
max_playtime: game.max_playtime
|
max_playtime: game.max_playtime,
|
||||||
})
|
})
|
||||||
.onConflictDoUpdate({
|
.onConflictDoUpdate({
|
||||||
target: games.id,
|
target: games.id,
|
||||||
|
|
@ -244,27 +251,30 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
|
||||||
min_players: game.min_players,
|
min_players: game.min_players,
|
||||||
max_players: game.max_players,
|
max_players: game.max_players,
|
||||||
min_playtime: game.min_playtime,
|
min_playtime: game.min_playtime,
|
||||||
max_playtime: game.max_playtime
|
max_playtime: game.max_playtime,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
const dbExternalIds = await transaction
|
const dbExternalIds = await transaction
|
||||||
.insert(externalIds)
|
.insert(externalIds)
|
||||||
.values({
|
.values({
|
||||||
externalId,
|
externalId,
|
||||||
type: 'game'
|
type: 'game',
|
||||||
})
|
})
|
||||||
.onConflictDoNothing()
|
.onConflictDoNothing()
|
||||||
.returning({ id: externalIds.id });
|
.returning({ id: externalIds.id });
|
||||||
await transaction.insert(gamesToExternalIds).values({
|
await transaction
|
||||||
gameId: dbGames[0].id,
|
.insert(gamesToExternalIds)
|
||||||
externalId: dbExternalIds[0].id
|
.values({
|
||||||
}).onConflictDoNothing();
|
gameId: dbGames[0].id,
|
||||||
|
externalId: dbExternalIds[0].id,
|
||||||
|
})
|
||||||
|
.onConflictDoNothing();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbGames.length === 0) {
|
if (dbGames.length === 0) {
|
||||||
return new Response('Could not create game', {
|
return new Response('Could not create game', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,142 +308,142 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
|
||||||
min_players: game.min_players,
|
min_players: game.min_players,
|
||||||
max_players: game.max_players,
|
max_players: game.max_players,
|
||||||
min_playtime: game.min_playtime,
|
min_playtime: game.min_playtime,
|
||||||
max_playtime: game.max_playtime
|
max_playtime: game.max_playtime,
|
||||||
})
|
})
|
||||||
.where(eq(games.id, id))
|
.where(eq(games.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
return new Response(JSON.stringify(dbGame[0]), {
|
return new Response(JSON.stringify(dbGame[0]), {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return new Response('Could not get publishers', {
|
return new Response('Could not get publishers', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('Creating or updating game', JSON.stringify(game, null, 2));
|
// console.log('Creating or updating game', JSON.stringify(game, null, 2));
|
||||||
// const categoryIds = game.categories;
|
// const categoryIds = game.categories;
|
||||||
// const mechanicIds = game.mechanics;
|
// const mechanicIds = game.mechanics;
|
||||||
// const publisherIds = game.publishers;
|
// const publisherIds = game.publishers;
|
||||||
// const designerIds = game.designers;
|
// const designerIds = game.designers;
|
||||||
// const artistIds = game.artists;
|
// const artistIds = game.artists;
|
||||||
// // const expansionIds = game.expansions;
|
// // const expansionIds = game.expansions;
|
||||||
// const externalUrl = `https://boardgamegeek.com/boardgame/${game.external_id}`;
|
// const externalUrl = `https://boardgamegeek.com/boardgame/${game.external_id}`;
|
||||||
// console.log('categoryIds', categoryIds);
|
// console.log('categoryIds', categoryIds);
|
||||||
// console.log('mechanicIds', mechanicIds);
|
// console.log('mechanicIds', mechanicIds);
|
||||||
// await db.transaction(async (transaction) => {
|
// await db.transaction(async (transaction) => {
|
||||||
// const dbGame = await db.transaction(async (transaction) => {
|
// const dbGame = await db.transaction(async (transaction) => {
|
||||||
// transaction.insert(games).values({
|
// transaction.insert(games).values({
|
||||||
// name: game.name,
|
// name: game.name,
|
||||||
// slug: kebabCase(game.name || ''),
|
// slug: kebabCase(game.name || ''),
|
||||||
// description: game.description,
|
// description: game.description,
|
||||||
// external_id: game.external_id,
|
// external_id: game.external_id,
|
||||||
// url: externalUrl,
|
// url: externalUrl,
|
||||||
// thumb_url: game.thumb_url,
|
// thumb_url: game.thumb_url,
|
||||||
// image_url: game.image_url,
|
// image_url: game.image_url,
|
||||||
// min_age: game.min_age || 0,
|
// min_age: game.min_age || 0,
|
||||||
// min_players: game.min_players || 0,
|
// min_players: game.min_players || 0,
|
||||||
// max_players: game.max_players || 0,
|
// max_players: game.max_players || 0,
|
||||||
// min_playtime: game.min_playtime || 0,
|
// min_playtime: game.min_playtime || 0,
|
||||||
// max_playtime: game.max_playtime || 0,
|
// max_playtime: game.max_playtime || 0,
|
||||||
// year_published: game.year_published || 0,
|
// year_published: game.year_published || 0,
|
||||||
// last_sync_at: new Date(),
|
// last_sync_at: new Date(),
|
||||||
// }).onConflictDoUpdate({
|
// }).onConflictDoUpdate({
|
||||||
// target: games.id, set: {
|
// target: games.id, set: {
|
||||||
// name: game.name,
|
// name: game.name,
|
||||||
// slug: kebabCase(game.name),
|
// slug: kebabCase(game.name),
|
||||||
// description: game.description,
|
// description: game.description,
|
||||||
// external_id: game.external_id,
|
// external_id: game.external_id,
|
||||||
// url: externalUrl,
|
// url: externalUrl,
|
||||||
// thumb_url: game.thumb_url,
|
// thumb_url: game.thumb_url,
|
||||||
// image_url: game.image_url,
|
// image_url: game.image_url,
|
||||||
// min_age: game.min_age || 0,
|
// min_age: game.min_age || 0,
|
||||||
// min_players: game.min_players || 0,
|
// min_players: game.min_players || 0,
|
||||||
// max_players: game.max_players || 0,
|
// max_players: game.max_players || 0,
|
||||||
// min_playtime: game.min_playtime || 0,
|
// min_playtime: game.min_playtime || 0,
|
||||||
// max_playtime: game.max_playtime || 0,
|
// max_playtime: game.max_playtime || 0,
|
||||||
// year_published: game.year_published || 0,
|
// year_published: game.year_published || 0,
|
||||||
// last_sync_at: new Date(),
|
// last_sync_at: new Date(),
|
||||||
// }
|
// }
|
||||||
// }).returning();
|
// }).returning();
|
||||||
// });
|
// });
|
||||||
// // TODO: Connect to everything else
|
// // TODO: Connect to everything else
|
||||||
// });
|
// });
|
||||||
// await db.insert(games).values({
|
// await db.insert(games).values({
|
||||||
// include: {
|
// include: {
|
||||||
// mechanics: true,
|
// mechanics: true,
|
||||||
// publishers: true,
|
// publishers: true,
|
||||||
// designers: true,
|
// designers: true,
|
||||||
// artists: true,
|
// artists: true,
|
||||||
// expansions: true
|
// expansions: true
|
||||||
// },
|
// },
|
||||||
// where: {
|
// where: {
|
||||||
// external_id: game.external_id
|
// external_id: game.external_id
|
||||||
// },
|
// },
|
||||||
// create: {
|
// create: {
|
||||||
// name: game.name,
|
// name: game.name,
|
||||||
// slug: kebabCase(game.name),
|
// slug: kebabCase(game.name),
|
||||||
// description: game.description,
|
// description: game.description,
|
||||||
// external_id: game.external_id,
|
// external_id: game.external_id,
|
||||||
// url: externalUrl,
|
// url: externalUrl,
|
||||||
// thumb_url: game.thumb_url,
|
// thumb_url: game.thumb_url,
|
||||||
// image_url: game.image_url,
|
// image_url: game.image_url,
|
||||||
// min_age: game.min_age || 0,
|
// min_age: game.min_age || 0,
|
||||||
// min_players: game.min_players || 0,
|
// min_players: game.min_players || 0,
|
||||||
// max_players: game.max_players || 0,
|
// max_players: game.max_players || 0,
|
||||||
// min_playtime: game.min_playtime || 0,
|
// min_playtime: game.min_playtime || 0,
|
||||||
// max_playtime: game.max_playtime || 0,
|
// max_playtime: game.max_playtime || 0,
|
||||||
// year_published: game.year_published || 0,
|
// year_published: game.year_published || 0,
|
||||||
// last_sync_at: new Date(),
|
// last_sync_at: new Date(),
|
||||||
// categories: {
|
// categories: {
|
||||||
// connect: categoryIds
|
// connect: categoryIds
|
||||||
// },
|
// },
|
||||||
// mechanics: {
|
// mechanics: {
|
||||||
// connect: mechanicIds
|
// connect: mechanicIds
|
||||||
// },
|
// },
|
||||||
// publishers: {
|
// publishers: {
|
||||||
// connect: publisherIds
|
// connect: publisherIds
|
||||||
// },
|
// },
|
||||||
// designers: {
|
// designers: {
|
||||||
// connect: designerIds
|
// connect: designerIds
|
||||||
// },
|
// },
|
||||||
// artists: {
|
// artists: {
|
||||||
// connect: artistIds
|
// connect: artistIds
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// update: {
|
// update: {
|
||||||
// name: game.name,
|
// name: game.name,
|
||||||
// slug: kebabCase(game.name),
|
// slug: kebabCase(game.name),
|
||||||
// description: game.description,
|
// description: game.description,
|
||||||
// external_id: game.external_id,
|
// external_id: game.external_id,
|
||||||
// url: externalUrl,
|
// url: externalUrl,
|
||||||
// thumb_url: game.thumb_url,
|
// thumb_url: game.thumb_url,
|
||||||
// image_url: game.image_url,
|
// image_url: game.image_url,
|
||||||
// min_age: game.min_age || 0,
|
// min_age: game.min_age || 0,
|
||||||
// min_players: game.min_players || 0,
|
// min_players: game.min_players || 0,
|
||||||
// max_players: game.max_players || 0,
|
// max_players: game.max_players || 0,
|
||||||
// min_playtime: game.min_playtime || 0,
|
// min_playtime: game.min_playtime || 0,
|
||||||
// max_playtime: game.max_playtime || 0,
|
// max_playtime: game.max_playtime || 0,
|
||||||
// year_published: game.year_published || 0,
|
// year_published: game.year_published || 0,
|
||||||
// last_sync_at: new Date(),
|
// last_sync_at: new Date(),
|
||||||
// categories: {
|
// categories: {
|
||||||
// connect: categoryIds
|
// connect: categoryIds
|
||||||
// },
|
// },
|
||||||
// mechanics: {
|
// mechanics: {
|
||||||
// connect: mechanicIds
|
// connect: mechanicIds
|
||||||
// },
|
// },
|
||||||
// publishers: {
|
// publishers: {
|
||||||
// connect: publisherIds
|
// connect: publisherIds
|
||||||
// },
|
// },
|
||||||
// designers: {
|
// designers: {
|
||||||
// connect: designerIds
|
// connect: designerIds
|
||||||
// },
|
// },
|
||||||
// artists: {
|
// artists: {
|
||||||
// connect: artistIds
|
// connect: artistIds
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import kebabCase from 'just-kebab-case';
|
import kebabCase from 'just-kebab-case';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../db';
|
||||||
import { externalIds, mechanics, mechanicsToExternalIds, type Mechanics } from '../../../schema';
|
import { externalIds, mechanics, mechanicsToExternalIds, type Mechanics } from '$db/schema';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
|
|
@ -12,7 +12,7 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbExternalId = await db.query.externalIds.findFirst({
|
const dbExternalId = await db.query.externalIds.findFirst({
|
||||||
where: eq(externalIds.externalId, externalId)
|
where: eq(externalIds.externalId, externalId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbExternalId) {
|
if (dbExternalId) {
|
||||||
|
|
@ -20,7 +20,7 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex
|
||||||
.select({
|
.select({
|
||||||
id: mechanics.id,
|
id: mechanics.id,
|
||||||
name: mechanics.name,
|
name: mechanics.name,
|
||||||
slug: mechanics.slug
|
slug: mechanics.slug,
|
||||||
})
|
})
|
||||||
.from(mechanics)
|
.from(mechanics)
|
||||||
.leftJoin(mechanicsToExternalIds, eq(mechanicsToExternalIds.externalId, externalId));
|
.leftJoin(mechanicsToExternalIds, eq(mechanicsToExternalIds.externalId, externalId));
|
||||||
|
|
@ -30,9 +30,9 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex
|
||||||
return new Response('Mechanic already exists', {
|
return new Response('Mechanic already exists', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Location: `${PUBLIC_SITE_URL}/api/mechanic/${foundMechanic[0].id}`
|
Location: `${PUBLIC_SITE_URL}/api/mechanic/${foundMechanic[0].id}`,
|
||||||
},
|
},
|
||||||
status: 409
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,25 +44,25 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex
|
||||||
.insert(mechanics)
|
.insert(mechanics)
|
||||||
.values({
|
.values({
|
||||||
name: mechanic.name,
|
name: mechanic.name,
|
||||||
slug: kebabCase(mechanic.name || mechanic.slug || '')
|
slug: kebabCase(mechanic.name || mechanic.slug || ''),
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
const dbExternalIds = await transaction
|
const dbExternalIds = await transaction
|
||||||
.insert(externalIds)
|
.insert(externalIds)
|
||||||
.values({
|
.values({
|
||||||
externalId,
|
externalId,
|
||||||
type: 'mechanic'
|
type: 'mechanic',
|
||||||
})
|
})
|
||||||
.returning({ id: externalIds.id });
|
.returning({ id: externalIds.id });
|
||||||
await transaction.insert(mechanicsToExternalIds).values({
|
await transaction.insert(mechanicsToExternalIds).values({
|
||||||
mechanicId: dbMechanics[0].id,
|
mechanicId: dbMechanics[0].id,
|
||||||
externalId: dbExternalIds[0].id
|
externalId: dbExternalIds[0].id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbMechanics.length === 0) {
|
if (dbMechanics.length === 0) {
|
||||||
return new Response('Could not create mechanic', {
|
return new Response('Could not create mechanic', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import kebabCase from 'just-kebab-case';
|
import kebabCase from 'just-kebab-case';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../db';
|
||||||
import {
|
import { externalIds, publishersToExternalIds, type Publishers, publishers } from '$db/schema';
|
||||||
externalIds,
|
|
||||||
publishersToExternalIds,
|
|
||||||
type Publishers,
|
|
||||||
publishers,
|
|
||||||
} from '../../../schema';
|
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
|
|
||||||
export async function getPublisher(locals: App.Locals, id: string) {
|
export async function getPublisher(locals: App.Locals, id: string) {
|
||||||
|
|
@ -17,8 +12,8 @@ export async function getPublisher(locals: App.Locals, id: string) {
|
||||||
}
|
}
|
||||||
return new Response(JSON.stringify(publisher[0]), {
|
return new Response(JSON.stringify(publisher[0]), {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,19 +27,19 @@ export async function updatePublisher(locals: App.Locals, publisher: Publishers,
|
||||||
.update(publishers)
|
.update(publishers)
|
||||||
.set({
|
.set({
|
||||||
name: publisher.name,
|
name: publisher.name,
|
||||||
slug: kebabCase(publisher.name || '')
|
slug: kebabCase(publisher.name || ''),
|
||||||
})
|
})
|
||||||
.where(eq(publishers.id, id))
|
.where(eq(publishers.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
return new Response(JSON.stringify(dbPublisher[0]), {
|
return new Response(JSON.stringify(dbPublisher[0]), {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return new Response('Could not get publishers', {
|
return new Response('Could not get publishers', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +47,7 @@ export async function updatePublisher(locals: App.Locals, publisher: Publishers,
|
||||||
export async function createPublisher(
|
export async function createPublisher(
|
||||||
locals: App.Locals,
|
locals: App.Locals,
|
||||||
publisher: Publishers,
|
publisher: Publishers,
|
||||||
externalId: string
|
externalId: string,
|
||||||
) {
|
) {
|
||||||
if (!publisher || !externalId || externalId === '') {
|
if (!publisher || !externalId || externalId === '') {
|
||||||
error(400, 'Invalid Request');
|
error(400, 'Invalid Request');
|
||||||
|
|
@ -60,7 +55,7 @@ export async function createPublisher(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbExternalId = await db.query.externalIds.findFirst({
|
const dbExternalId = await db.query.externalIds.findFirst({
|
||||||
where: eq(externalIds.externalId, externalId)
|
where: eq(externalIds.externalId, externalId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbExternalId) {
|
if (dbExternalId) {
|
||||||
|
|
@ -68,7 +63,7 @@ export async function createPublisher(
|
||||||
.select({
|
.select({
|
||||||
id: publishers.id,
|
id: publishers.id,
|
||||||
name: publishers.name,
|
name: publishers.name,
|
||||||
slug: publishers.slug
|
slug: publishers.slug,
|
||||||
})
|
})
|
||||||
.from(publishers)
|
.from(publishers)
|
||||||
.leftJoin(publishersToExternalIds, eq(publishersToExternalIds.externalId, externalId));
|
.leftJoin(publishersToExternalIds, eq(publishersToExternalIds.externalId, externalId));
|
||||||
|
|
@ -78,9 +73,9 @@ export async function createPublisher(
|
||||||
return new Response('Publisher already exists', {
|
return new Response('Publisher already exists', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Location: `${PUBLIC_SITE_URL}/api/publisher/${foundPublisher[0].id}`
|
Location: `${PUBLIC_SITE_URL}/api/publisher/${foundPublisher[0].id}`,
|
||||||
},
|
},
|
||||||
status: 409
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,25 +87,25 @@ export async function createPublisher(
|
||||||
.insert(publishers)
|
.insert(publishers)
|
||||||
.values({
|
.values({
|
||||||
name: publisher.name,
|
name: publisher.name,
|
||||||
slug: kebabCase(publisher.name || publisher.slug || '')
|
slug: kebabCase(publisher.name || publisher.slug || ''),
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
const dbExternalIds = await transaction
|
const dbExternalIds = await transaction
|
||||||
.insert(externalIds)
|
.insert(externalIds)
|
||||||
.values({
|
.values({
|
||||||
externalId,
|
externalId,
|
||||||
type: 'publisher'
|
type: 'publisher',
|
||||||
})
|
})
|
||||||
.returning({ id: externalIds.id });
|
.returning({ id: externalIds.id });
|
||||||
await transaction.insert(publishersToExternalIds).values({
|
await transaction.insert(publishersToExternalIds).values({
|
||||||
publisherId: dbPublishers[0].id,
|
publisherId: dbPublishers[0].id,
|
||||||
externalId: dbExternalIds[0].id
|
externalId: dbExternalIds[0].id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (dbPublishers.length === 0) {
|
if (dbPublishers.length === 0) {
|
||||||
return new Response('Could not create publisher', {
|
return new Response('Could not create publisher', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { GameType, SavedGameType } from '$lib/types';
|
import type { GameType, SavedGameType } from '$lib/types';
|
||||||
import kebabCase from 'just-kebab-case';
|
import kebabCase from 'just-kebab-case';
|
||||||
import type { Games } from '../../schema';
|
import type { Games } from '$db/schema';
|
||||||
|
|
||||||
export function convertToSavedGame(game: GameType | SavedGameType): SavedGameType {
|
export function convertToSavedGame(game: GameType | SavedGameType): SavedGameType {
|
||||||
return {
|
return {
|
||||||
|
|
@ -9,7 +9,7 @@ export function convertToSavedGame(game: GameType | SavedGameType): SavedGameTyp
|
||||||
thumb_url: game.thumb_url,
|
thumb_url: game.thumb_url,
|
||||||
players: game.players,
|
players: game.players,
|
||||||
playtime: game.playtime,
|
playtime: game.playtime,
|
||||||
searchTerms: `${game.name.toLowerCase()}`
|
searchTerms: `${game.name.toLowerCase()}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ export function mapSavedGameToGame(game: SavedGameType): GameType {
|
||||||
description: '',
|
description: '',
|
||||||
description_preview: '',
|
description_preview: '',
|
||||||
players,
|
players,
|
||||||
playtime
|
playtime,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,6 +56,6 @@ export function mapAPIGameToBoredGame(game: GameType): Games {
|
||||||
min_playtime: game.min_playtime,
|
min_playtime: game.min_playtime,
|
||||||
max_playtime: game.max_playtime,
|
max_playtime: game.max_playtime,
|
||||||
min_age: game.min_age,
|
min_age: game.min_age,
|
||||||
description: game.description
|
description: game.description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { redirect, loadFlash } from 'sveltekit-flash-message/server';
|
import { redirect, loadFlash } from 'sveltekit-flash-message/server';
|
||||||
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages';
|
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../db';
|
||||||
import { user_roles } from '../../../../schema';
|
import { user_roles } from '$db/schema';
|
||||||
|
|
||||||
export const load = loadFlash(async (event) => {
|
export const load = loadFlash(async (event) => {
|
||||||
const { locals } = event;
|
const { locals } = event;
|
||||||
|
|
@ -16,10 +16,10 @@ export const load = loadFlash(async (event) => {
|
||||||
with: {
|
with: {
|
||||||
role: {
|
role: {
|
||||||
columns: {
|
columns: {
|
||||||
name: true
|
name: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const containsAdminRole = userRoles.some((user_role) => user_role?.role?.name === 'admin');
|
const containsAdminRole = userRoles.some((user_role) => user_role?.role?.name === 'admin');
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,20 @@
|
||||||
import { redirect } from "sveltekit-flash-message/server";
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import type { PageServerLoad } from "./$types";
|
import type { PageServerLoad } from './$types';
|
||||||
import { notSignedInMessage } from "$lib/flashMessages";
|
import { notSignedInMessage } from '$lib/flashMessages';
|
||||||
import db from "$lib/drizzle";
|
import db from '../../../../../db';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
|
|
||||||
// TODO: Ensure admin user
|
// TODO: Ensure admin user
|
||||||
if (!event.locals.user) {
|
if (!event.locals.user) {
|
||||||
redirect(302, '/login', notSignedInMessage, event);
|
redirect(302, '/login', notSignedInMessage, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = await db.query
|
const users = await db.query.users.findMany({
|
||||||
.users
|
limit: 10,
|
||||||
.findMany({
|
offset: 0,
|
||||||
limit: 10,
|
});
|
||||||
offset: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
users
|
users,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -2,8 +2,8 @@ import { and, eq, inArray, not } from 'drizzle-orm';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages';
|
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../../../db';
|
||||||
import { roles, user_roles, users } from '../../../../../../schema';
|
import { roles, user_roles, users } from '$db/schema';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
const { params } = event;
|
const { params } = event;
|
||||||
|
|
@ -22,16 +22,16 @@ export const load: PageServerLoad = async (event) => {
|
||||||
role: {
|
role: {
|
||||||
columns: {
|
columns: {
|
||||||
name: true,
|
name: true,
|
||||||
cuid: true
|
cuid: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const containsAdminRole = foundUser?.user_roles?.some(
|
const containsAdminRole = foundUser?.user_roles?.some(
|
||||||
(user_role) => user_role?.role?.name === 'admin'
|
(user_role) => user_role?.role?.name === 'admin',
|
||||||
);
|
);
|
||||||
if (!containsAdminRole) {
|
if (!containsAdminRole) {
|
||||||
console.log('Not an admin');
|
console.log('Not an admin');
|
||||||
|
|
@ -45,14 +45,14 @@ export const load: PageServerLoad = async (event) => {
|
||||||
where: not(inArray(roles.cuid, currentRoleIds)),
|
where: not(inArray(roles.cuid, currentRoleIds)),
|
||||||
columns: {
|
columns: {
|
||||||
name: true,
|
name: true,
|
||||||
cuid: true
|
cuid: true,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: foundUser,
|
user: foundUser,
|
||||||
availableRoles
|
availableRoles,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -71,10 +71,10 @@ export const actions = {
|
||||||
role: {
|
role: {
|
||||||
columns: {
|
columns: {
|
||||||
name: true,
|
name: true,
|
||||||
cuid: true
|
cuid: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('userRoles', userRoles);
|
console.log('userRoles', userRoles);
|
||||||
|
|
@ -88,13 +88,13 @@ export const actions = {
|
||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const role = data.get('role');
|
const role = data.get('role');
|
||||||
const dbRole = await db.query.roles.findFirst({
|
const dbRole = await db.query.roles.findFirst({
|
||||||
where: eq(roles.cuid, role?.toString() ?? '')
|
where: eq(roles.cuid, role?.toString() ?? ''),
|
||||||
});
|
});
|
||||||
console.log('dbRole', dbRole);
|
console.log('dbRole', dbRole);
|
||||||
if (dbRole) {
|
if (dbRole) {
|
||||||
await db.insert(user_roles).values({
|
await db.insert(user_roles).values({
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
role_id: dbRole.id
|
role_id: dbRole.id,
|
||||||
});
|
});
|
||||||
redirect({ type: 'success', message: `Successfully added role ${dbRole.name}!` }, event);
|
redirect({ type: 'success', message: `Successfully added role ${dbRole.name}!` }, event);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -114,10 +114,10 @@ export const actions = {
|
||||||
role: {
|
role: {
|
||||||
columns: {
|
columns: {
|
||||||
name: true,
|
name: true,
|
||||||
cuid: true
|
cuid: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const containsAdminRole = userRoles.some((user_role) => user_role?.role?.name === 'admin');
|
const containsAdminRole = userRoles.some((user_role) => user_role?.role?.name === 'admin');
|
||||||
|
|
@ -128,7 +128,7 @@ export const actions = {
|
||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const role = data.get('role');
|
const role = data.get('role');
|
||||||
const dbRole = await db.query.roles.findFirst({
|
const dbRole = await db.query.roles.findFirst({
|
||||||
where: eq(roles.cuid, role?.toString() ?? '')
|
where: eq(roles.cuid, role?.toString() ?? ''),
|
||||||
});
|
});
|
||||||
console.log('dbRole', dbRole);
|
console.log('dbRole', dbRole);
|
||||||
if (dbRole) {
|
if (dbRole) {
|
||||||
|
|
@ -139,5 +139,5 @@ export const actions = {
|
||||||
} else {
|
} else {
|
||||||
redirect({ type: 'error', message: `Failed to remove role ${dbRole.name} !` }, event);
|
redirect({ type: 'error', message: `Failed to remove role ${dbRole.name} !` }, event);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
import { Input } from "$lib/components/ui/input";
|
import { Input } from "$lib/components/ui/input";
|
||||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||||
import DataTableCheckbox from "./user-table-checkbox.svelte";
|
import DataTableCheckbox from "./user-table-checkbox.svelte";
|
||||||
import type { Users } from '../../../../../schema';
|
import type { Users } from '$db/schema';
|
||||||
|
|
||||||
export let users: Users[] = [];
|
export let users: Users[] = [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { modifyListGameSchema, type ListGame } from '$lib/validations/zod-schemas';
|
import { modifyListGameSchema, type ListGame } from '$lib/validations/zod-schemas';
|
||||||
import { search_schema } from '$lib/zodValidation.js';
|
import { search_schema } from '$lib/zodValidation.js';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../db';
|
||||||
import { collection_items, collections, games } from '../../../../schema';
|
import { collection_items, collections, games } from '$db/schema';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages';
|
import { notSignedInMessage } from '$lib/flashMessages';
|
||||||
|
|
||||||
export async function load(event) {
|
export async function load(event) {
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { superValidate } from 'sveltekit-superforms/server';
|
import { superValidate } from 'sveltekit-superforms/server';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { type ListGame, modifyListGameSchema } from '$lib/validations/zod-schemas';
|
import { type ListGame, modifyListGameSchema } from '$lib/validations/zod-schemas';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../../db';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages.js';
|
import { notSignedInMessage } from '$lib/flashMessages.js';
|
||||||
import { collections, games, collection_items } from '../../../../../schema.js';
|
import { collections, games, collection_items } from '$db/schema';
|
||||||
import { search_schema } from '$lib/zodValidation';
|
import { search_schema } from '$lib/zodValidation';
|
||||||
import type { UICollection } from '$lib/types';
|
import type { UICollection } from '$lib/types';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { fail } from '@sveltejs/kit';
|
import { fail } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../db';
|
||||||
import { wishlists } from '../../../../schema.js';
|
import { wishlists } from '$db/schema';
|
||||||
|
|
||||||
export async function load({ locals }) {
|
export async function load({ locals }) {
|
||||||
if (!locals.user) {
|
if (!locals.user) {
|
||||||
|
|
@ -10,16 +10,16 @@ export async function load({ locals }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userWishlists = await db.query.wishlists.findMany({
|
const userWishlists = await db.query.wishlists.findMany({
|
||||||
where: eq(wishlists.user_id, locals.user.id)
|
where: eq(wishlists.user_id, locals.user.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wishlsits: userWishlists
|
wishlsits: userWishlists,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
wishlists: []
|
wishlists: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { type Actions, fail, redirect } from "@sveltejs/kit";
|
import { type Actions, fail, redirect } from '@sveltejs/kit';
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from 'drizzle-orm';
|
||||||
import { zod } from "sveltekit-superforms/adapters";
|
import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { superValidate } from 'sveltekit-superforms/server';
|
import { superValidate } from 'sveltekit-superforms/server';
|
||||||
import db from "$lib/drizzle.js";
|
import db from '../../../../../db';
|
||||||
import { modifyListGameSchema } from "$lib/validations/zod-schemas";
|
import { modifyListGameSchema } from '$lib/validations/zod-schemas';
|
||||||
import { games, wishlist_items, wishlists } from "../../../../../schema.js";
|
import { games, wishlist_items, wishlists } from '$db/schema';
|
||||||
|
|
||||||
export async function load({ params, locals }) {
|
export async function load({ params, locals }) {
|
||||||
const user = locals.user;
|
const user = locals.user;
|
||||||
|
|
@ -13,20 +13,22 @@ export async function load({ params, locals }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const wishlist = await db.select({
|
const wishlist = await db
|
||||||
wishlistId: wishlists.id,
|
.select({
|
||||||
wishlistItems: {
|
wishlistId: wishlists.id,
|
||||||
id: wishlist_items.id,
|
wishlistItems: {
|
||||||
gameId: wishlist_items.game_id,
|
id: wishlist_items.id,
|
||||||
gameName: games.name,
|
gameId: wishlist_items.game_id,
|
||||||
gameThumbUrl: games.thumb_url
|
gameName: games.name,
|
||||||
},
|
gameThumbUrl: games.thumb_url,
|
||||||
}).from(wishlists)
|
},
|
||||||
.leftJoin(wishlist_items, eq(wishlists.id, wishlist_items.wishlist_id))
|
})
|
||||||
.leftJoin(games, eq(games.id, wishlist_items.game_id))
|
.from(wishlists)
|
||||||
.where(eq(wishlists.id, params.id));
|
.leftJoin(wishlist_items, eq(wishlists.id, wishlist_items.wishlist_id))
|
||||||
|
.leftJoin(games, eq(games.id, wishlist_items.game_id))
|
||||||
|
.where(eq(wishlists.id, params.id));
|
||||||
return {
|
return {
|
||||||
wishlist
|
wishlist,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
@ -46,27 +48,27 @@ export const actions: Actions = {
|
||||||
|
|
||||||
if (!params?.id) {
|
if (!params?.id) {
|
||||||
throw fail(400, {
|
throw fail(400, {
|
||||||
message: 'Invalid Request'
|
message: 'Invalid Request',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const game = await db.query.games.findFirst({
|
const game = await db.query.games.findFirst({
|
||||||
where: eq(games.id, form.id)
|
where: eq(games.id, form.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!game) {
|
if (!game) {
|
||||||
return fail(400, {
|
return fail(400, {
|
||||||
message: 'Game not found'
|
message: 'Game not found',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const wishlist = await db.query.wishlists.findFirst({
|
const wishlist = await db.query.wishlists.findFirst({
|
||||||
where: eq(wishlists.id, params.id)
|
where: eq(wishlists.id, params.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (wishlist?.user_id !== locals.user.id) {
|
if (wishlist?.user_id !== locals.user.id) {
|
||||||
return fail(401, {
|
return fail(401, {
|
||||||
message: 'Unauthorized'
|
message: 'Unauthorized',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,17 +78,17 @@ export const actions: Actions = {
|
||||||
|
|
||||||
const wishlistItem = await db.insert(wishlist_items).values({
|
const wishlistItem = await db.insert(wishlist_items).values({
|
||||||
game_id: game.id,
|
game_id: game.id,
|
||||||
wishlist_id: wishlist.id
|
wishlist_id: wishlist.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!wishlistItem) {
|
if (!wishlistItem) {
|
||||||
return fail(500, {
|
return fail(500, {
|
||||||
message: 'Something went wrong'
|
message: 'Something went wrong',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
form
|
form,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// Create new wishlist
|
// Create new wishlist
|
||||||
|
|
@ -96,15 +98,15 @@ export const actions: Actions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Delete a wishlist
|
// Delete a wishlist
|
||||||
delete: async ({ locals}) => {
|
delete: async ({ locals }) => {
|
||||||
if (!locals.user) {
|
if (!locals.user) {
|
||||||
throw fail(401);
|
throw fail(401);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Remove game from a wishlist
|
// Remove game from a wishlist
|
||||||
remove: async ({ locals }) => {
|
remove: async ({ locals }) => {
|
||||||
if (!locals.user) {
|
if (!locals.user) {
|
||||||
throw fail(401);
|
throw fail(401);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ import { message, setError, superValidate } from 'sveltekit-superforms/server';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { changeEmailSchema, profileSchema } from '$lib/validations/account';
|
import { changeEmailSchema, profileSchema } from '$lib/validations/account';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages';
|
import { notSignedInMessage } from '$lib/flashMessages';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../db';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import { users } from '../../../../schema';
|
import { users } from '$db/schema';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
if (!event.locals.user) {
|
if (!event.locals.user) {
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { Argon2id } from 'oslo/password';
|
import { Argon2id } from 'oslo/password';
|
||||||
import type { PageServerLoad } from '../../../$types';
|
import type { PageServerLoad } from '../../../$types';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../../../../db';
|
||||||
import { changeUserPasswordSchema } from '$lib/validations/account';
|
import { changeUserPasswordSchema } from '$lib/validations/account';
|
||||||
import { lucia } from '$lib/server/auth.js';
|
import { lucia } from '$lib/server/auth.js';
|
||||||
import { users } from '../../../../../../../schema';
|
import { users } from '$db/schema';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages';
|
import { notSignedInMessage } from '$lib/flashMessages';
|
||||||
import type { Cookie } from 'lucia';
|
import type { Cookie } from 'lucia';
|
||||||
|
|
||||||
|
|
@ -23,10 +23,10 @@ export const load: PageServerLoad = async (event) => {
|
||||||
form.data = {
|
form.data = {
|
||||||
current_password: '',
|
current_password: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirm_password: ''
|
confirm_password: '',
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
form
|
form,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ export const actions: Actions = {
|
||||||
|
|
||||||
if (!form.valid) {
|
if (!form.valid) {
|
||||||
return fail(400, {
|
return fail(400, {
|
||||||
form
|
form,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ export const actions: Actions = {
|
||||||
const user = event.locals.user;
|
const user = event.locals.user;
|
||||||
|
|
||||||
const dbUser = await db.query.users.findFirst({
|
const dbUser = await db.query.users.findFirst({
|
||||||
where: eq(users.id, user.id)
|
where: eq(users.id, user.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dbUser?.hashed_password) {
|
if (!dbUser?.hashed_password) {
|
||||||
|
|
@ -61,13 +61,13 @@ export const actions: Actions = {
|
||||||
form.data.current_password = '';
|
form.data.current_password = '';
|
||||||
return setError(
|
return setError(
|
||||||
form,
|
form,
|
||||||
'Error occurred. Please try again or contact support if you need further help.'
|
'Error occurred. Please try again or contact support if you need further help.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentPasswordVerified = await new Argon2id().verify(
|
const currentPasswordVerified = await new Argon2id().verify(
|
||||||
dbUser.hashed_password,
|
dbUser.hashed_password,
|
||||||
form.data.current_password
|
form.data.current_password,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!currentPasswordVerified) {
|
if (!currentPasswordVerified) {
|
||||||
|
|
@ -86,7 +86,7 @@ export const actions: Actions = {
|
||||||
.set({ hashed_password: hashedPassword })
|
.set({ hashed_password: hashedPassword })
|
||||||
.where(eq(users.id, user.id));
|
.where(eq(users.id, user.id));
|
||||||
await lucia.createSession(user.id, {
|
await lucia.createSession(user.id, {
|
||||||
country: event.locals.session?.ipCountry ?? 'unknown'
|
country: event.locals.session?.ipCountry ?? 'unknown',
|
||||||
});
|
});
|
||||||
sessionCookie = lucia.createBlankSessionCookie();
|
sessionCookie = lucia.createBlankSessionCookie();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -98,23 +98,23 @@ export const actions: Actions = {
|
||||||
}
|
}
|
||||||
event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||||
path: '.',
|
path: '.',
|
||||||
...sessionCookie.attributes
|
...sessionCookie.attributes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const message = {
|
const message = {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: 'Password Updated. Please sign in.'
|
message: 'Password Updated. Please sign in.',
|
||||||
} as const;
|
} as const;
|
||||||
redirect(302, '/login', message, event);
|
redirect(302, '/login', message, event);
|
||||||
}
|
}
|
||||||
return setError(
|
return setError(
|
||||||
form,
|
form,
|
||||||
'Error occurred. Please try again or contact support if you need further help.'
|
'Error occurred. Please try again or contact support if you need further help.',
|
||||||
);
|
);
|
||||||
// TODO: Add toast instead?
|
// TODO: Add toast instead?
|
||||||
// form.data.password = '';
|
// form.data.password = '';
|
||||||
// form.data.confirm_password = '';
|
// form.data.confirm_password = '';
|
||||||
// form.data.current_password = '';
|
// form.data.current_password = '';
|
||||||
// return message(form, 'Profile updated successfully.');
|
// return message(form, 'Profile updated successfully.');
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ import { redirect, setFlash } from 'sveltekit-flash-message/server';
|
||||||
import type { PageServerLoad } from '../../$types';
|
import type { PageServerLoad } from '../../$types';
|
||||||
import { addTwoFactorSchema, removeTwoFactorSchema } from '$lib/validations/account';
|
import { addTwoFactorSchema, removeTwoFactorSchema } from '$lib/validations/account';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages';
|
import { notSignedInMessage } from '$lib/flashMessages';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../../../db';
|
||||||
import { recovery_codes, users } from '../../../../../../schema';
|
import { recovery_codes, users } from '$db/schema';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema));
|
const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema));
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../../../../db';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { Argon2id } from 'oslo/password';
|
import { Argon2id } from 'oslo/password';
|
||||||
import { alphabet, generateRandomString } from 'oslo/crypto';
|
import { alphabet, generateRandomString } from 'oslo/crypto';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages';
|
import { notSignedInMessage } from '$lib/flashMessages';
|
||||||
import type { PageServerLoad } from '../../../$types';
|
import type { PageServerLoad } from '../../../$types';
|
||||||
import { recovery_codes, users } from '../../../../../../../schema';
|
import { recovery_codes, users } from '$db/schema';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
const user = event.locals.user;
|
const user = event.locals.user;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { superValidate } from 'sveltekit-superforms/server';
|
import { superValidate } from 'sveltekit-superforms/server';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { modifyListGameSchema } from '$lib/validations/zod-schemas';
|
import { modifyListGameSchema } from '$lib/validations/zod-schemas';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../db';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages.js';
|
import { notSignedInMessage } from '$lib/flashMessages.js';
|
||||||
import { games, wishlist_items, wishlists } from '../../../../schema.js';
|
import { games, wishlist_items, wishlists } from '$db/schema';
|
||||||
|
|
||||||
export async function load(event) {
|
export async function load(event) {
|
||||||
const user = event.locals.user;
|
const user = event.locals.user;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { superValidate } from 'sveltekit-superforms/server';
|
import { superValidate } from 'sveltekit-superforms/server';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { modifyListGameSchema } from '$lib/validations/zod-schemas';
|
import { modifyListGameSchema } from '$lib/validations/zod-schemas';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../../db';
|
||||||
import { notSignedInMessage } from '$lib/flashMessages.js';
|
import { notSignedInMessage } from '$lib/flashMessages.js';
|
||||||
import { games, wishlist_items, wishlists } from '../../../../../schema.js';
|
import { games, wishlist_items, wishlists } from '$db/schema';
|
||||||
|
|
||||||
export async function load(event) {
|
export async function load(event) {
|
||||||
const { params, locals } = event;
|
const { params, locals } = event;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { MetaTagsProps } from 'svelte-meta-tags';
|
import type { MetaTagsProps } from 'svelte-meta-tags';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../db';
|
||||||
import { collections, wishlists } from '../../schema';
|
import { collections, wishlists } from '$db/schema';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals, url }) => {
|
export const load: PageServerLoad = async ({ locals, url }) => {
|
||||||
const image = {
|
const image = {
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,16 @@ import { createMechanic } from '$lib/utils/db/mechanicUtils';
|
||||||
import { createPublisher } from '$lib/utils/db/publisherUtils';
|
import { createPublisher } from '$lib/utils/db/publisherUtils';
|
||||||
import { createExpansion } from '$lib/utils/db/expansionUtils';
|
import { createExpansion } from '$lib/utils/db/expansionUtils';
|
||||||
import { createOrUpdateGame } from '$lib/utils/db/gameUtils';
|
import { createOrUpdateGame } from '$lib/utils/db/gameUtils';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../../db';
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
import { collection_items, collections, expansions, games, wishlist_items, wishlists } from '../../../../schema';
|
import {
|
||||||
|
collection_items,
|
||||||
|
collections,
|
||||||
|
expansions,
|
||||||
|
games,
|
||||||
|
wishlist_items,
|
||||||
|
wishlists,
|
||||||
|
} from '$db/schema';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -22,32 +29,32 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
||||||
publisher: {
|
publisher: {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true
|
name: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mechanics_to_games: {
|
mechanics_to_games: {
|
||||||
with: {
|
with: {
|
||||||
mechanic: {
|
mechanic: {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true
|
name: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
categories_to_games: {
|
categories_to_games: {
|
||||||
with: {
|
with: {
|
||||||
category: {
|
category: {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true
|
name: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
console.log('found game', game);
|
console.log('found game', game);
|
||||||
|
|
||||||
|
|
@ -71,10 +78,10 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
thumb_url: true
|
thumb_url: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let collectionItem;
|
let collectionItem;
|
||||||
|
|
@ -87,8 +94,11 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
||||||
// TODO: Select wishlist items based on wishlist
|
// TODO: Select wishlist items based on wishlist
|
||||||
if (wishlist) {
|
if (wishlist) {
|
||||||
wishlistItem = await db.query.wishlist_items.findFirst({
|
wishlistItem = await db.query.wishlist_items.findFirst({
|
||||||
where: and(eq(wishlist_items.wishlist_id, wishlist.id), eq(wishlist_items.game_id, game.id)),
|
where: and(
|
||||||
})
|
eq(wishlist_items.wishlist_id, wishlist.id),
|
||||||
|
eq(wishlist_items.game_id, game.id),
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const collection = await db.query.collections.findFirst({
|
const collection = await db.query.collections.findFirst({
|
||||||
|
|
@ -99,8 +109,11 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
||||||
|
|
||||||
if (collection) {
|
if (collection) {
|
||||||
collectionItem = await db.query.collection_items.findFirst({
|
collectionItem = await db.query.collection_items.findFirst({
|
||||||
where: and(eq(collection_items.collection_id, collection.id), eq(collection_items.game_id, game.id)),
|
where: and(
|
||||||
})
|
eq(collection_items.collection_id, collection.id),
|
||||||
|
eq(collection_items.game_id, game.id),
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +135,7 @@ export const load: PageServerLoad = async ({ params, locals, fetch }) => {
|
||||||
|
|
||||||
async function syncGameAndConnectedData(locals: App.Locals, game: Game, eventFetch: Function) {
|
async function syncGameAndConnectedData(locals: App.Locals, game: Game, eventFetch: Function) {
|
||||||
console.log(
|
console.log(
|
||||||
`Retrieving full external game details for external id: ${game.external_id} with name ${game.name}`
|
`Retrieving full external game details for external id: ${game.external_id} with name ${game.name}`,
|
||||||
);
|
);
|
||||||
const externalGameResponse = await eventFetch(`/api/external/game/${game.external_id}`);
|
const externalGameResponse = await eventFetch(`/api/external/game/${game.external_id}`);
|
||||||
if (externalGameResponse.ok) {
|
if (externalGameResponse.ok) {
|
||||||
|
|
@ -134,7 +147,7 @@ async function syncGameAndConnectedData(locals: App.Locals, game: Game, eventFet
|
||||||
for (const externalCategory of externalGame.categories) {
|
for (const externalCategory of externalGame.categories) {
|
||||||
const category = await createCategory(locals, externalCategory, externalGame.external_id);
|
const category = await createCategory(locals, externalCategory, externalGame.external_id);
|
||||||
categories.push({
|
categories.push({
|
||||||
id: category.id
|
id: category.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (const externalMechanic of externalGame.mechanics) {
|
for (const externalMechanic of externalGame.mechanics) {
|
||||||
|
|
@ -151,7 +164,7 @@ async function syncGameAndConnectedData(locals: App.Locals, game: Game, eventFet
|
||||||
if (externalExpansion?.inbound === true) {
|
if (externalExpansion?.inbound === true) {
|
||||||
createExpansion(locals, externalExpansion);
|
createExpansion(locals, externalExpansion);
|
||||||
} else {
|
} else {
|
||||||
createExpansion(locals,externalExpansion);
|
createExpansion(locals, externalExpansion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { RateLimiter } from 'sveltekit-rate-limiter/server';
|
import { RateLimiter } from 'sveltekit-rate-limiter/server';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../db';
|
||||||
import { lucia } from '$lib/server/auth';
|
import { lucia } from '$lib/server/auth';
|
||||||
import { signInSchema } from '$lib/validations/auth';
|
import { signInSchema } from '$lib/validations/auth';
|
||||||
import { users, recovery_codes } from '../../../schema';
|
import { users, recovery_codes } from '$db/schema';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
import {fail, error, type Actions} from '@sveltejs/kit';
|
import { fail, error, type Actions } from '@sveltejs/kit';
|
||||||
import {Argon2id} from 'oslo/password';
|
import { Argon2id } from 'oslo/password';
|
||||||
import {eq} from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import {zod} from 'sveltekit-superforms/adapters';
|
import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import {setError, superValidate} from 'sveltekit-superforms/server';
|
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||||
import {redirect} from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import {RateLimiter} from 'sveltekit-rate-limiter/server';
|
import { RateLimiter } from 'sveltekit-rate-limiter/server';
|
||||||
import type {PageServerLoad} from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import {lucia} from '$lib/server/auth';
|
import { lucia } from '$lib/server/auth';
|
||||||
import {signUpSchema} from '$lib/validations/auth';
|
import { signUpSchema } from '$lib/validations/auth';
|
||||||
import {add_user_to_role} from '$server/roles';
|
import { add_user_to_role } from '$server/roles';
|
||||||
import db from '$lib/drizzle';
|
import db from '../../../db';
|
||||||
import {collections, users, wishlists} from '../../../schema';
|
import { collections, users, wishlists } from '$db/schema';
|
||||||
import {createId as cuid2} from '@paralleldrive/cuid2';
|
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||||
|
|
||||||
const limiter = new RateLimiter({
|
const limiter = new RateLimiter({
|
||||||
// A rate is defined by [number, unit]
|
// A rate is defined by [number, unit]
|
||||||
IPUA: [5, 'm']
|
IPUA: [5, 'm'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const signUpDefaults = {
|
const signUpDefaults = {
|
||||||
|
|
@ -25,7 +25,7 @@ const signUpDefaults = {
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirm_password: '',
|
confirm_password: '',
|
||||||
terms: true
|
terms: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
|
|
@ -37,14 +37,14 @@ export const load: PageServerLoad = async (event) => {
|
||||||
// );
|
// );
|
||||||
|
|
||||||
if (event.locals.user) {
|
if (event.locals.user) {
|
||||||
const message = {type: 'success', message: 'You are already signed in'} as const;
|
const message = { type: 'success', message: 'You are already signed in' } as const;
|
||||||
throw redirect('/', message, event);
|
throw redirect('/', message, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
form: await superValidate(zod(signUpSchema), {
|
form: await superValidate(zod(signUpSchema), {
|
||||||
defaults: signUpDefaults
|
defaults: signUpDefaults,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ export const actions: Actions = {
|
||||||
form.data.password = '';
|
form.data.password = '';
|
||||||
form.data.confirm_password = '';
|
form.data.confirm_password = '';
|
||||||
return fail(400, {
|
return fail(400, {
|
||||||
form
|
form,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ export const actions: Actions = {
|
||||||
console.log('Check if user already exists');
|
console.log('Check if user already exists');
|
||||||
|
|
||||||
const existing_user = await db.query.users.findFirst({
|
const existing_user = await db.query.users.findFirst({
|
||||||
where: eq(users.username, form.data.username)
|
where: eq(users.username, form.data.username),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing_user) {
|
if (existing_user) {
|
||||||
|
|
@ -81,41 +81,41 @@ export const actions: Actions = {
|
||||||
const hashedPassword = await new Argon2id().hash(form.data.password);
|
const hashedPassword = await new Argon2id().hash(form.data.password);
|
||||||
|
|
||||||
const user = await db
|
const user = await db
|
||||||
.insert(users)
|
.insert(users)
|
||||||
.values({
|
.values({
|
||||||
username: form.data.username,
|
username: form.data.username,
|
||||||
hashed_password: hashedPassword,
|
hashed_password: hashedPassword,
|
||||||
email: form.data.email,
|
email: form.data.email,
|
||||||
first_name: form.data.firstName ?? '',
|
first_name: form.data.firstName ?? '',
|
||||||
last_name: form.data.lastName ?? '',
|
last_name: form.data.lastName ?? '',
|
||||||
verified: false,
|
verified: false,
|
||||||
receive_email: false,
|
receive_email: false,
|
||||||
theme: 'system',
|
theme: 'system',
|
||||||
two_factor_secret: '',
|
two_factor_secret: '',
|
||||||
two_factor_enabled: false
|
two_factor_enabled: false,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
console.log('signup user', user);
|
console.log('signup user', user);
|
||||||
|
|
||||||
if (!user || user.length === 0) {
|
if (!user || user.length === 0) {
|
||||||
return fail(400, {
|
return fail(400, {
|
||||||
form,
|
form,
|
||||||
message: `Could not create your account. Please try again. If the problem persists, please contact support. Error ID: ${cuid2()}`
|
message: `Could not create your account. Please try again. If the problem persists, please contact support. Error ID: ${cuid2()}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
add_user_to_role(user[0].id, 'user', true);
|
add_user_to_role(user[0].id, 'user', true);
|
||||||
await db.insert(collections).values({
|
await db.insert(collections).values({
|
||||||
user_id: user[0].id
|
user_id: user[0].id,
|
||||||
});
|
});
|
||||||
await db.insert(wishlists).values({
|
await db.insert(wishlists).values({
|
||||||
user_id: user[0].id
|
user_id: user[0].id,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session = await lucia.createSession(user[0].id, {
|
session = await lucia.createSession(user[0].id, {
|
||||||
ip_country: event.locals.ip,
|
ip_country: event.locals.ip,
|
||||||
ip_address: event.locals.country
|
ip_address: event.locals.country,
|
||||||
});
|
});
|
||||||
sessionCookie = lucia.createSessionCookie(session.id);
|
sessionCookie = lucia.createSessionCookie(session.id);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|
@ -126,7 +126,7 @@ export const actions: Actions = {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
const message = {
|
const message = {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'Unable to create your account. Please try again.'
|
message: 'Unable to create your account. Please try again.',
|
||||||
};
|
};
|
||||||
form.data.password = '';
|
form.data.password = '';
|
||||||
form.data.confirm_password = '';
|
form.data.confirm_password = '';
|
||||||
|
|
@ -135,11 +135,11 @@ export const actions: Actions = {
|
||||||
|
|
||||||
event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||||
path: '.',
|
path: '.',
|
||||||
...sessionCookie.attributes
|
...sessionCookie.attributes,
|
||||||
});
|
});
|
||||||
|
|
||||||
redirect(302, '/');
|
redirect(302, '/');
|
||||||
// const message = { type: 'success', message: 'Signed Up!' } as const;
|
// const message = { type: 'success', message: 'Signed Up!' } as const;
|
||||||
// throw flashRedirect(message, event);
|
// throw flashRedirect(message, event);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../db';
|
||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { users } from '../../../../schema.js';
|
import { users } from '$db/schema';
|
||||||
import { createPasswordResetToken } from '$lib/server/auth-utils.js';
|
import { createPasswordResetToken } from '$lib/server/auth-utils.js';
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
|
|
||||||
|
|
@ -13,11 +13,13 @@ export async function POST({ locals, request }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await db.query.users.findFirst({
|
const user = await db.query.users.findFirst({
|
||||||
where: eq(users.email, email)
|
where: eq(users.email, email),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
error(200, { message: 'Email sent! Please check your email for a link to reset your password.' });
|
error(200, {
|
||||||
|
message: 'Email sent! Please check your email for a link to reset your password.',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const verificationToken = await createPasswordResetToken(user.id);
|
const verificationToken = await createPasswordResetToken(user.id);
|
||||||
|
|
@ -27,6 +29,6 @@ export async function POST({ locals, request }) {
|
||||||
console.log('Verification link: ' + verificationLink);
|
console.log('Verification link: ' + verificationLink);
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 200
|
status: 200,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../../db';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { password_reset_tokens, users } from '../../../../../schema.js';
|
import { password_reset_tokens, users } from '$db/schema';
|
||||||
import { isWithinExpirationDate } from 'oslo';
|
import { isWithinExpirationDate } from 'oslo';
|
||||||
import { lucia } from '$lib/server/auth.js';
|
import { lucia } from '$lib/server/auth.js';
|
||||||
import { Argon2id } from 'oslo/password';
|
import { Argon2id } from 'oslo/password';
|
||||||
|
|
@ -10,34 +10,31 @@ export async function POST({ request, params }) {
|
||||||
|
|
||||||
if (typeof password !== 'string' || password.length < 8) {
|
if (typeof password !== 'string' || password.length < 8) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 400
|
status: 400,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const verificationToken = params.token;
|
const verificationToken = params.token;
|
||||||
|
|
||||||
const token = await db.query.password_reset_tokens.findFirst({
|
const token = await db.query.password_reset_tokens.findFirst({
|
||||||
where: eq(password_reset_tokens.id, verificationToken)
|
where: eq(password_reset_tokens.id, verificationToken),
|
||||||
});
|
});
|
||||||
if (!token) {
|
if (!token) {
|
||||||
await db.delete(password_reset_tokens).where(eq(password_reset_tokens.id, verificationToken));
|
await db.delete(password_reset_tokens).where(eq(password_reset_tokens.id, verificationToken));
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 400
|
status: 400,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!token?.expires_at || !isWithinExpirationDate(token.expires_at)) {
|
if (!token?.expires_at || !isWithinExpirationDate(token.expires_at)) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 400
|
status: 400,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await lucia.invalidateUserSessions(token.user_id);
|
await lucia.invalidateUserSessions(token.user_id);
|
||||||
const hashPassword = await new Argon2id().hash(password);
|
const hashPassword = await new Argon2id().hash(password);
|
||||||
await db
|
await db.update(users).set({ hashed_password: hashPassword }).where(eq(users.id, token.user_id));
|
||||||
.update(users)
|
|
||||||
.set({ hashed_password: hashPassword })
|
|
||||||
.where(eq(users.id, token.user_id));
|
|
||||||
|
|
||||||
const session = await lucia.createSession(token.user_id, {});
|
const session = await lucia.createSession(token.user_id, {});
|
||||||
const sessionCookie = lucia.createSessionCookie(session.id);
|
const sessionCookie = lucia.createSessionCookie(session.id);
|
||||||
|
|
@ -45,8 +42,8 @@ export async function POST({ request, params }) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 302,
|
status: 302,
|
||||||
headers: {
|
headers: {
|
||||||
Location: "/",
|
Location: '/',
|
||||||
"Set-Cookie": sessionCookie.serialize()
|
'Set-Cookie': sessionCookie.serialize(),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../../db';
|
||||||
import { collection_items, users } from '../../../../../schema.js';
|
import { collection_items, users } from '$db/schema';
|
||||||
|
|
||||||
// Search a user's collection
|
// Search a user's collection
|
||||||
export async function GET({ url, locals, params }) {
|
export async function GET({ url, locals, params }) {
|
||||||
|
|
@ -20,7 +20,7 @@ export async function GET({ url, locals, params }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const collection = await db.query.collections.findFirst({
|
const collection = await db.query.collections.findFirst({
|
||||||
where: eq(users.id, locals?.user?.id)
|
where: eq(users.id, locals?.user?.id),
|
||||||
});
|
});
|
||||||
console.log('collection', collection);
|
console.log('collection', collection);
|
||||||
|
|
||||||
|
|
@ -37,12 +37,13 @@ export async function GET({ url, locals, params }) {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
thumb_url: true
|
thumb_url: true,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
orderBy: (collection_items, { asc, desc }) => {
|
orderBy: (collection_items, { asc, desc }) => {
|
||||||
const dbSort = sort === 'dateAdded' ? collection_items.created_at : collection_items.times_played;
|
const dbSort =
|
||||||
|
sort === 'dateAdded' ? collection_items.created_at : collection_items.times_played;
|
||||||
if (order === 'asc') {
|
if (order === 'asc') {
|
||||||
return asc(dbSort);
|
return asc(dbSort);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -50,7 +51,7 @@ export async function GET({ url, locals, params }) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
offset: skip,
|
offset: skip,
|
||||||
limit
|
limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
return json(userCollectionItems);
|
return json(userCollectionItems);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../db';
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
import { asc, count } from 'drizzle-orm';
|
import { asc, count } from 'drizzle-orm';
|
||||||
import { games, type Games } from '../../../../schema.js';
|
import { games, type Games } from '$db/schema';
|
||||||
|
|
||||||
export const GET = async ({ url }) => {
|
export const GET = async ({ url }) => {
|
||||||
const searchParams = Object.fromEntries(url.searchParams);
|
const searchParams = Object.fromEntries(url.searchParams);
|
||||||
|
|
@ -14,12 +14,13 @@ export const GET = async ({ url }) => {
|
||||||
try {
|
try {
|
||||||
const totalGames = await db
|
const totalGames = await db
|
||||||
.select({
|
.select({
|
||||||
value: count(games.id)
|
value: count(games.id),
|
||||||
})
|
})
|
||||||
.from(games);
|
.from(games);
|
||||||
const numberOfGames = totalGames[0].value || 0;
|
const numberOfGames = totalGames[0].value || 0;
|
||||||
const randomIndex = Math.floor(Math.random() * numberOfGames);
|
const randomIndex = Math.floor(Math.random() * numberOfGames);
|
||||||
const randomGames: Games[] = await db.select()
|
const randomGames: Games[] = await db
|
||||||
|
.select()
|
||||||
.from(games)
|
.from(games)
|
||||||
.orderBy(asc(games.id))
|
.orderBy(asc(games.id))
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
|
|
@ -29,4 +30,4 @@ export const GET = async ({ url }) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
throw error(500, { message: 'Something went wrong' });
|
throw error(500, { message: 'Something went wrong' });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../db';
|
||||||
import {asc, desc, eq, ilike, or } from 'drizzle-orm';
|
import { asc, desc, eq, ilike, or } from 'drizzle-orm';
|
||||||
import { games } from '../../../../schema.js';
|
import { games } from '$db/schema';
|
||||||
import kebabCase from "just-kebab-case";
|
import kebabCase from 'just-kebab-case';
|
||||||
|
|
||||||
// Search a user's collection
|
// Search a user's collection
|
||||||
export const GET = async ({ url, locals }) => {
|
export const GET = async ({ url, locals }) => {
|
||||||
|
|
@ -17,20 +17,21 @@ export const GET = async ({ url, locals }) => {
|
||||||
if (orderBy === 'name') {
|
if (orderBy === 'name') {
|
||||||
orderBy = 'slug';
|
orderBy = 'slug';
|
||||||
}
|
}
|
||||||
console.log(`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}, orderBy: ${orderBy}`);
|
console.log(
|
||||||
|
`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}, orderBy: ${orderBy}`,
|
||||||
|
);
|
||||||
console.log(exact);
|
console.log(exact);
|
||||||
if (exact) {
|
if (exact) {
|
||||||
console.log('Exact Search API');
|
console.log('Exact Search API');
|
||||||
const game =
|
const game = await db.query.games.findFirst({
|
||||||
await db.query.games.findFirst({
|
where: eq(games.name, q),
|
||||||
where: eq(games.name, q),
|
columns: {
|
||||||
columns: {
|
id: true,
|
||||||
id: true,
|
name: true,
|
||||||
name: true,
|
slug: true,
|
||||||
slug: true,
|
thumb_url: true,
|
||||||
thumb_url: true
|
},
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (!game) {
|
if (!game) {
|
||||||
error(404, { message: 'No games found' });
|
error(404, { message: 'No games found' });
|
||||||
|
|
@ -39,20 +40,19 @@ export const GET = async ({ url, locals }) => {
|
||||||
console.log('Games found in Exact Search API', JSON.stringify(foundGames, null, 2));
|
console.log('Games found in Exact Search API', JSON.stringify(foundGames, null, 2));
|
||||||
return json(foundGames);
|
return json(foundGames);
|
||||||
} else {
|
} else {
|
||||||
const foundGames = await db.select({
|
const foundGames =
|
||||||
id: games.id,
|
(await db
|
||||||
name: games.name,
|
.select({
|
||||||
slug: games.slug,
|
id: games.id,
|
||||||
thumb_url: games.thumb_url
|
name: games.name,
|
||||||
})
|
slug: games.slug,
|
||||||
|
thumb_url: games.thumb_url,
|
||||||
|
})
|
||||||
.from(games)
|
.from(games)
|
||||||
.where(or(
|
.where(or(ilike(games.name, `%${q}%`), ilike(games.slug, `%${kebabCase(q)}%`)))
|
||||||
ilike(games.name, `%${q}%`),
|
|
||||||
ilike(games.slug, `%${kebabCase(q)}%`)
|
|
||||||
))
|
|
||||||
.orderBy(getOrderDirection(order)(getOrderBy(orderBy)))
|
.orderBy(getOrderDirection(order)(getOrderBy(orderBy)))
|
||||||
.offset(skip)
|
.offset(skip)
|
||||||
.limit(limit) || [];
|
.limit(limit)) || [];
|
||||||
// const foundGames = await db.select({
|
// const foundGames = await db.select({
|
||||||
// id: games.id,
|
// id: games.id,
|
||||||
// name: games.name,
|
// name: games.name,
|
||||||
|
|
@ -73,7 +73,7 @@ export const GET = async ({ url, locals }) => {
|
||||||
type OrderDirection = 'asc' | 'desc';
|
type OrderDirection = 'asc' | 'desc';
|
||||||
|
|
||||||
const getOrderDirection = (direction: OrderDirection) => {
|
const getOrderDirection = (direction: OrderDirection) => {
|
||||||
return direction === 'asc' ? asc: desc;
|
return direction === 'asc' ? asc : desc;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrderBy = (orderBy: string) => {
|
const getOrderBy = (orderBy: string) => {
|
||||||
|
|
@ -85,4 +85,4 @@ const getOrderBy = (orderBy: string) => {
|
||||||
default:
|
default:
|
||||||
return games.slug;
|
return games.slug;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { createPublisher } from '$lib/utils/db/publisherUtils.js';
|
import { createPublisher } from '$lib/utils/db/publisherUtils.js';
|
||||||
import type { Publishers } from '../../../schema.js';
|
import type { Publishers } from '$db/schema';
|
||||||
|
|
||||||
type PublisherCreate = {
|
type PublisherCreate = {
|
||||||
publisher: Publishers;
|
publisher: Publishers;
|
||||||
externalId: string;
|
externalId: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export async function POST({ request, locals }) {
|
export async function POST({ request, locals }) {
|
||||||
const data: PublisherCreate = await request.json();
|
const data: PublisherCreate = await request.json();
|
||||||
|
|
@ -15,7 +15,7 @@ export async function POST({ request, locals }) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return new Response('Could not create publisher', {
|
return new Response('Could not create publisher', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getPublisher, updatePublisher } from '$lib/utils/db/publisherUtils.js';
|
import { getPublisher, updatePublisher } from '$lib/utils/db/publisherUtils.js';
|
||||||
import type { Publishers } from '../../../../schema.js';
|
import type { Publishers } from '$db/schema';
|
||||||
|
|
||||||
export async function GET({ locals, params }) {
|
export async function GET({ locals, params }) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -7,7 +7,7 @@ export async function GET({ locals, params }) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return new Response('Could not get publishers', {
|
return new Response('Could not get publishers', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '../../../../../db';
|
||||||
import { wishlist_items, wishlists } from '../../../../../schema.js';
|
import { wishlist_items, wishlists } from '$db/schema';
|
||||||
|
|
||||||
// Search a user's collection
|
// Search a user's collection
|
||||||
export async function GET({ url, locals, params }) {
|
export async function GET({ url, locals, params }) {
|
||||||
|
|
@ -16,12 +16,12 @@ export async function GET({ url, locals, params }) {
|
||||||
|
|
||||||
if (!locals.user) {
|
if (!locals.user) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 401
|
status: 401,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const wishlist = await db.query.wishlists.findFirst({
|
const wishlist = await db.query.wishlists.findFirst({
|
||||||
where: eq(wishlists.user_id, locals?.user?.id)
|
where: eq(wishlists.user_id, locals?.user?.id),
|
||||||
});
|
});
|
||||||
console.log('wishlist', wishlist);
|
console.log('wishlist', wishlist);
|
||||||
|
|
||||||
|
|
@ -38,9 +38,9 @@ export async function GET({ url, locals, params }) {
|
||||||
columns: {
|
columns: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
thumb_url: true
|
thumb_url: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
orderBy: (wishlist_items, { asc, desc }) => {
|
orderBy: (wishlist_items, { asc, desc }) => {
|
||||||
const dbSort = wishlist_items.created_at;
|
const dbSort = wishlist_items.created_at;
|
||||||
|
|
@ -51,7 +51,7 @@ export async function GET({ url, locals, params }) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
offset: skip,
|
offset: skip,
|
||||||
limit
|
limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
return json(itemsInWishlist);
|
return json(itemsInWishlist);
|
||||||
|
|
|
||||||
549
src/schema.ts
549
src/schema.ts
|
|
@ -1,549 +0,0 @@
|
||||||
import { relations, sql, type InferSelectModel } from 'drizzle-orm';
|
|
||||||
import {
|
|
||||||
pgTable,
|
|
||||||
timestamp,
|
|
||||||
text,
|
|
||||||
boolean,
|
|
||||||
integer,
|
|
||||||
index,
|
|
||||||
pgEnum,
|
|
||||||
primaryKey,
|
|
||||||
uuid,
|
|
||||||
} from 'drizzle-orm/pg-core';
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
|
||||||
import { tsvector } from './tsVector';
|
|
||||||
|
|
||||||
// User Related Schemas
|
|
||||||
|
|
||||||
export const users = pgTable('users', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
username: text('username').unique(),
|
|
||||||
hashed_password: text('hashed_password'),
|
|
||||||
email: text('email').unique(),
|
|
||||||
first_name: text('first_name'),
|
|
||||||
last_name: text('last_name'),
|
|
||||||
verified: boolean('verified').default(false),
|
|
||||||
receive_email: boolean('receive_email').default(false),
|
|
||||||
theme: text('theme').default('system'),
|
|
||||||
two_factor_secret: text('two_factor_secret').default(''),
|
|
||||||
two_factor_enabled: boolean('two_factor_enabled').default(false),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const recovery_codes = pgTable('recovery_codes', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
userId: uuid('user_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id),
|
|
||||||
code: text('code').notNull(),
|
|
||||||
used: boolean('used').default(false),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type RecoveryCodes = InferSelectModel<typeof recovery_codes>;
|
|
||||||
|
|
||||||
export const user_relations = relations(users, ({ many }) => ({
|
|
||||||
user_roles: many(user_roles),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export type Users = InferSelectModel<typeof users>;
|
|
||||||
|
|
||||||
export const sessions = pgTable('sessions', {
|
|
||||||
id: text('id').primaryKey(),
|
|
||||||
userId: uuid('user_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id),
|
|
||||||
expiresAt: timestamp('expires_at', {
|
|
||||||
withTimezone: true,
|
|
||||||
mode: 'date',
|
|
||||||
}).notNull(),
|
|
||||||
ipCountry: text('ip_country'),
|
|
||||||
ipAddress: text('ip_address'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const roles = pgTable('roles', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2())
|
|
||||||
.notNull(),
|
|
||||||
name: text('name').unique().notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
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: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
user_id: uuid('user_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: 'cascade' }),
|
|
||||||
role_id: uuid('role_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => roles.id, { onDelete: 'cascade' }),
|
|
||||||
primary: boolean('primary').default(false),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const user_role_relations = relations(user_roles, ({ one }) => ({
|
|
||||||
role: one(roles, {
|
|
||||||
fields: [user_roles.role_id],
|
|
||||||
references: [roles.id],
|
|
||||||
}),
|
|
||||||
user: one(users, {
|
|
||||||
fields: [user_roles.user_id],
|
|
||||||
references: [users.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export type UserRoles = InferSelectModel<typeof user_roles>;
|
|
||||||
|
|
||||||
export const password_reset_tokens = pgTable('password_reset_tokens', {
|
|
||||||
id: text('id')
|
|
||||||
.primaryKey()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
user_id: uuid('user_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: 'cascade' }),
|
|
||||||
expires_at: timestamp('expires_at', {
|
|
||||||
withTimezone: true,
|
|
||||||
mode: 'date',
|
|
||||||
precision: 6,
|
|
||||||
}),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({
|
|
||||||
user: one(users, {
|
|
||||||
fields: [password_reset_tokens.user_id],
|
|
||||||
references: [users.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const collections = pgTable('collections', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
user_id: uuid('user_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: 'cascade' }),
|
|
||||||
name: text('name').notNull().default('My Collection'),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const collection_relations = relations(collections, ({ one }) => ({
|
|
||||||
user: one(users, {
|
|
||||||
fields: [collections.user_id],
|
|
||||||
references: [users.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const collection_items = pgTable('collection_items', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
collection_id: uuid('collection_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => collections.id, { onDelete: 'cascade' }),
|
|
||||||
game_id: uuid('game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'cascade' }),
|
|
||||||
times_played: integer('times_played').default(0),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type CollectionItems = InferSelectModel<typeof collection_items>;
|
|
||||||
|
|
||||||
export const collection_item_relations = relations(collection_items, ({ one }) => ({
|
|
||||||
collection: one(collections, {
|
|
||||||
fields: [collection_items.collection_id],
|
|
||||||
references: [collections.id],
|
|
||||||
}),
|
|
||||||
game: one(games, {
|
|
||||||
fields: [collection_items.game_id],
|
|
||||||
references: [games.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const wishlists = pgTable('wishlists', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
user_id: uuid('user_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: 'cascade' }),
|
|
||||||
name: text('name').notNull().default('My Wishlist'),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Wishlists = InferSelectModel<typeof wishlists>;
|
|
||||||
|
|
||||||
export const wishlists_relations = relations(wishlists, ({ one }) => ({
|
|
||||||
user: one(users, {
|
|
||||||
fields: [wishlists.user_id],
|
|
||||||
references: [users.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const wishlist_items = pgTable('wishlist_items', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
wishlist_id: uuid('wishlist_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => wishlists.id, { onDelete: 'cascade' }),
|
|
||||||
game_id: uuid('game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'cascade' }),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type WishlistItems = InferSelectModel<typeof wishlist_items>;
|
|
||||||
|
|
||||||
export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
|
|
||||||
wishlist: one(wishlists, {
|
|
||||||
fields: [wishlist_items.wishlist_id],
|
|
||||||
references: [wishlists.id],
|
|
||||||
}),
|
|
||||||
game: one(games, {
|
|
||||||
fields: [wishlist_items.game_id],
|
|
||||||
references: [games.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 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: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
type: externalIdType('type').notNull(),
|
|
||||||
externalId: text('external_id').notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type ExternalIds = InferSelectModel<typeof externalIds>;
|
|
||||||
|
|
||||||
export const games = pgTable(
|
|
||||||
'games',
|
|
||||||
{
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
name: text('name'),
|
|
||||||
slug: text('slug'),
|
|
||||||
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: text('image_url'),
|
|
||||||
thumb_url: text('thumb_url'),
|
|
||||||
url: text('url'),
|
|
||||||
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').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
},
|
|
||||||
(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: uuid('game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
externalId: uuid('external_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
gamesToExternalIdsPkey: primaryKey({
|
|
||||||
columns: [table.gameId, table.externalId],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
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: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
base_game_id: uuid('base_game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
game_id: uuid('game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Expansions = InferSelectModel<typeof expansions>;
|
|
||||||
|
|
||||||
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 publishers = pgTable('publishers', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
name: text('name'),
|
|
||||||
slug: text('slug'),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Publishers = InferSelectModel<typeof publishers>;
|
|
||||||
|
|
||||||
export const publishersToExternalIds = pgTable(
|
|
||||||
'publishers_to_external_ids',
|
|
||||||
{
|
|
||||||
publisherId: uuid('publisher_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
externalId: uuid('external_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
publishersToExternalIdsPkey: primaryKey({
|
|
||||||
columns: [table.publisherId, table.externalId],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const publishers_relations = relations(publishers, ({ many }) => ({
|
|
||||||
publishers_to_games: many(publishers_to_games),
|
|
||||||
publishersToExternalIds: many(publishersToExternalIds),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const categories = pgTable('categories', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
name: text('name'),
|
|
||||||
slug: text('slug'),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Categories = InferSelectModel<typeof categories>;
|
|
||||||
|
|
||||||
export const categoriesToExternalIds = pgTable(
|
|
||||||
'categories_to_external_ids',
|
|
||||||
{
|
|
||||||
categoryId: uuid('category_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
externalId: uuid('external_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
categoriesToExternalIdsPkey: primaryKey({
|
|
||||||
columns: [table.categoryId, table.externalId],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const categories_to_games = pgTable(
|
|
||||||
'categories_to_games',
|
|
||||||
{
|
|
||||||
category_id: uuid('category_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
game_id: uuid('game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
categoriesToGamesPkey: primaryKey({
|
|
||||||
columns: [table.category_id, table.game_id],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
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),
|
|
||||||
categoriesToExternalIds: many(categoriesToExternalIds),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const mechanics = pgTable('mechanics', {
|
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
|
||||||
cuid: text('cuid')
|
|
||||||
.unique()
|
|
||||||
.$defaultFn(() => cuid2()),
|
|
||||||
name: text('name'),
|
|
||||||
slug: text('slug'),
|
|
||||||
created_at: timestamp('created_at').notNull().defaultNow(),
|
|
||||||
updated_at: timestamp('updated_at').notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Mechanics = InferSelectModel<typeof mechanics>;
|
|
||||||
|
|
||||||
export const mechanicsToExternalIds = pgTable(
|
|
||||||
'mechanics_to_external_ids',
|
|
||||||
{
|
|
||||||
mechanicId: uuid('mechanic_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
externalId: uuid('external_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
mechanicsToExternalIdsPkey: primaryKey({
|
|
||||||
columns: [table.mechanicId, table.externalId],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const mechanic_relations = relations(mechanics, ({ many }) => ({
|
|
||||||
mechanics_to_games: many(mechanics_to_games),
|
|
||||||
mechanicsToExternalIds: many(mechanicsToExternalIds),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const mechanics_to_games = pgTable(
|
|
||||||
'mechanics_to_games',
|
|
||||||
{
|
|
||||||
mechanic_id: uuid('mechanic_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
game_id: uuid('game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
mechanicsToGamesPkey: primaryKey({
|
|
||||||
columns: [table.mechanic_id, table.game_id],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const mechanics_to_games_relations = relations(mechanics_to_games, ({ one }) => ({
|
|
||||||
mechanic: one(mechanics, {
|
|
||||||
fields: [mechanics_to_games.mechanic_id],
|
|
||||||
references: [mechanics.id],
|
|
||||||
}),
|
|
||||||
game: one(games, {
|
|
||||||
fields: [mechanics_to_games.game_id],
|
|
||||||
references: [games.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const publishers_to_games = pgTable(
|
|
||||||
'publishers_to_games',
|
|
||||||
{
|
|
||||||
publisher_id: uuid('publisher_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
game_id: uuid('game_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
|
||||||
},
|
|
||||||
(table) => {
|
|
||||||
return {
|
|
||||||
publishersToGamesPkey: primaryKey({
|
|
||||||
columns: [table.publisher_id, table.game_id],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({
|
|
||||||
publisher: one(publishers, {
|
|
||||||
fields: [publishers_to_games.publisher_id],
|
|
||||||
references: [publishers.id],
|
|
||||||
}),
|
|
||||||
game: one(games, {
|
|
||||||
fields: [publishers_to_games.game_id],
|
|
||||||
references: [games.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import db from '$lib/drizzle';
|
import db from '../db';
|
||||||
import { roles, user_roles } from '../schema';
|
import { roles, user_roles } from '$db/schema';
|
||||||
|
|
||||||
export async function add_user_to_role(user_id: string, role_name: string, primary = false) {
|
export async function add_user_to_role(user_id: string, role_name: string, primary = false) {
|
||||||
// Find the role by its name
|
// Find the role by its name
|
||||||
const role = await db.query.roles.findFirst({
|
const role = await db.query.roles.findFirst({
|
||||||
where: eq(roles.name, role_name)
|
where: eq(roles.name, role_name),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!role || !role.id) {
|
if (!role || !role.id) {
|
||||||
|
|
@ -16,6 +16,6 @@ export async function add_user_to_role(user_id: string, role_name: string, prima
|
||||||
return db.insert(user_roles).values({
|
return db.insert(user_roles).values({
|
||||||
user_id,
|
user_id,
|
||||||
role_id: role.id,
|
role_id: role.id,
|
||||||
primary
|
primary,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
import db from '$lib/drizzle';
|
import db from '../db';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { users, type Users } from '../schema';
|
import { users, type Users } from '$db/schema';
|
||||||
import { add_user_to_role } from './roles';
|
import { add_user_to_role } from './roles';
|
||||||
|
|
||||||
export function create_user(user: Users) {
|
export function create_user(user: Users) {
|
||||||
return db.insert(users).values({
|
return db.insert(users).values({
|
||||||
username: user.username
|
username: user.username,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function find_or_create_user(user: Users) {
|
export async function find_or_create_user(user: Users) {
|
||||||
const existing_user = await db.query.users.findFirst({
|
const existing_user = await db.query.users.findFirst({
|
||||||
where: eq(users.username, user.username)
|
where: eq(users.username, user.username),
|
||||||
});
|
});
|
||||||
if (existing_user) {
|
if (existing_user) {
|
||||||
return existing_user;
|
return existing_user;
|
||||||
|
|
@ -30,12 +30,12 @@ export async function find_user_with_roles(user_id: string) {
|
||||||
with: {
|
with: {
|
||||||
role: {
|
role: {
|
||||||
select: {
|
select: {
|
||||||
name: true
|
name: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
if (!user_with_roles) {
|
if (!user_with_roles) {
|
||||||
throw new Error('User not found');
|
throw new Error('User not found');
|
||||||
|
|
@ -43,6 +43,6 @@ export async function find_user_with_roles(user_id: string) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...user_with_roles,
|
...user_with_roles,
|
||||||
roles: user_with_roles.role.map((user_role) => user_role.role.name)
|
roles: user_with_roles.role.map((user_role) => user_role.role.name),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ const config = {
|
||||||
inspector: {
|
inspector: {
|
||||||
toggleKeyCombo: 'control-alt-shift',
|
toggleKeyCombo: 'control-alt-shift',
|
||||||
showToggleButton: 'always',
|
showToggleButton: 'always',
|
||||||
toggleButtonPos: 'bottom-right'
|
toggleButtonPos: 'bottom-right',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
|
|
@ -21,19 +21,20 @@ const config = {
|
||||||
$assets: './src/assets',
|
$assets: './src/assets',
|
||||||
$components: './src/components',
|
$components: './src/components',
|
||||||
'$components/*': 'src/lib/components/*',
|
'$components/*': 'src/lib/components/*',
|
||||||
|
$db: './src/db',
|
||||||
$server: './src/server',
|
$server: './src/server',
|
||||||
$lib: './src/lib',
|
$lib: './src/lib',
|
||||||
$state: './src/state',
|
$state: './src/state',
|
||||||
$styles: './src/styles',
|
$styles: './src/styles',
|
||||||
$themes: './src/themes'
|
$themes: './src/themes',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
shadcn: {
|
shadcn: {
|
||||||
componentPath: './src/lib/components/ui'
|
componentPath: './src/lib/components/ui',
|
||||||
},
|
},
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
enableSourcemap: true,
|
enableSourcemap: true,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue