Refactor hono again.

This commit is contained in:
Bradley Shellnut 2024-09-04 16:04:41 -07:00
parent 679f88d50d
commit e723c7b66a
122 changed files with 1420 additions and 10361 deletions

30
.env.example Normal file
View file

@ -0,0 +1,30 @@
# Private
ORIGIN=http://localhost:5173
NODE_ENV=development
DATABASE_USER='postgres'
DATABASE_PASSWORD='postgres'
DATABASE_HOST='localhost'
DATABASE_PORT=5432
DATABASE_DB='postgres'
REDIS_URL='redis://127.0.0.1:6379/0'
DB_MIGRATING='false'
DB_SEEDING='false'
ADMIN_USERNAME=
ADMIN_PASSWORD=
TWO_FACTOR_TIMEOUT=300000
# Public
PUBLIC_SITE_NAME='Bored Game'
PUBLIC_SITE_URL='http://localhost:5173'
PUBLIC_UMAMI_DO_NOT_TRACK=true
PUBLIC_UMAMI_URL=
PUBLIC_UMAMI_ID=
# quick setting for key-combo only
SVELTE_INSPECTOR_TOGGLE=control-shift-i

View file

@ -5,7 +5,7 @@ import env from './src/env'
export default defineConfig({
dialect: 'postgresql',
out: './src/lib/server/api/databases/migrations',
schema: './src/lib/server/api/databases/tables/drizzle.ts',
schema: './src/lib/server/api/databases/tables/*.table.ts',
dbCredentials: {
host: env.DATABASE_HOST || 'localhost',
port: Number(env.DATABASE_PORT) || 5432,

View file

@ -1,20 +1,20 @@
import { error, json } from '@sveltejs/kit';
import { getGame } from '$lib/utils/db/gameUtils.js';
import { getGame } from '$lib/utils/db/gameUtils.js'
import { error, json } from '@sveltejs/kit'
export const GET = async ({ locals, params }) => {
const game_id = Number(params.id).valueOf();
const game_id = Number(params.id).valueOf()
// TODO: Debounce excessive calls and possibly throttle
if (isNaN(game_id) || !isFinite(game_id)) {
error(400, { message: 'Invalid game id' });
error(400, { message: 'Invalid game id' })
}
try {
return json(await getGame(locals, params.id));
return json(await getGame(locals, params.id))
} catch (e) {
console.error(e);
return new Response('Could not get games', {
status: 500
});
console.error(e)
return new Response('Could not get gamesTable', {
status: 500,
})
}
}
}

View file

@ -1,44 +1,34 @@
import { error, json } from '@sveltejs/kit';
import db from '../../../../db';
import { asc, desc, eq, ilike, or } from 'drizzle-orm';
import { games } from '$db/schema';
import kebabCase from 'just-kebab-case';
import {
FilterSchema,
PaginationSchema,
SearchSchema,
SortSchema,
} from '$lib/validations/zod-schemas';
import { games } from '$db/schema'
import { FilterSchema, PaginationSchema, SearchSchema, SortSchema } from '$lib/validations/zod-schemas'
import { error, json } from '@sveltejs/kit'
import { asc, desc, eq, ilike, or } from 'drizzle-orm'
import kebabCase from 'just-kebab-case'
import db from '../../../../db'
// Search a user's collection
export const GET = async ({ url, locals }) => {
const searchParams = Object.fromEntries(url.searchParams);
const searchParams = Object.fromEntries(url.searchParams)
const searchGames = PaginationSchema.merge(FilterSchema)
.merge(SortSchema)
.merge(SearchSchema)
.parse(searchParams);
const searchGames = PaginationSchema.merge(FilterSchema).merge(SortSchema).merge(SearchSchema).parse(searchParams)
if (searchGames.status !== 'success') {
error(400, 'Invalid request');
error(400, 'Invalid request')
}
const q = searchParams?.q?.trim() || '';
const limit = parseInt(searchParams?.limit) || 10;
const skip = parseInt(searchParams?.skip) || 0;
const order: OrderDirection = searchParams?.order === 'desc' ? 'desc' : 'asc';
const exact = searchParams?.exact === 'true';
let orderBy = searchParams?.orderBy || 'slug';
const q = searchParams?.q?.trim() || ''
const limit = parseInt(searchParams?.limit) || 10
const skip = parseInt(searchParams?.skip) || 0
const order: OrderDirection = searchParams?.order === 'desc' ? 'desc' : 'asc'
const exact = searchParams?.exact === 'true'
let orderBy = searchParams?.orderBy || 'slug'
if (orderBy === 'name') {
orderBy = 'slug';
orderBy = 'slug'
}
console.log(
`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}, orderBy: ${orderBy}`,
);
console.log(exact);
console.log(`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}, orderBy: ${orderBy}`)
console.log(exact)
if (exact) {
console.log('Exact Search API');
console.log('Exact Search API')
const game = await db.query.games.findFirst({
where: eq(games.name, q),
columns: {
@ -47,14 +37,14 @@ export const GET = async ({ url, locals }) => {
slug: true,
thumb_url: true,
},
});
})
if (!game) {
error(404, { message: 'No games found' });
error(404, { message: 'No gamesTable found' })
}
const foundGames = [game];
console.log('Games found in Exact Search API', JSON.stringify(foundGames, null, 2));
return json(foundGames);
const foundGames = [game]
console.log('Games found in Exact Search API', JSON.stringify(foundGames, null, 2))
return json(foundGames)
} else {
const foundGames =
(await db
@ -68,37 +58,37 @@ export const GET = async ({ url, locals }) => {
.where(or(ilike(games.name, `%${q}%`), ilike(games.slug, `%${kebabCase(q)}%`)))
.orderBy(getOrderDirection(order)(getOrderBy(orderBy)))
.offset(skip)
.limit(limit)) || [];
.limit(limit)) || []
// const foundGames = await db.select({
// id: games.id,
// name: games.name,
// slug: games.slug,
// thumb_url: games.thumb_url
// id: gamesTable.id,
// name: gamesTable.name,
// slug: gamesTable.slug,
// thumb_url: gamesTable.thumb_url
// })
// .from(games)
// .where(sql`to_tsvector('simple', ${games.name}) || to_tsvector('simple', ${games.slug}) @@ to_tsquery('simple', ${q})`)
// .from(gamesTable)
// .where(sql`to_tsvector('simple', ${gamesTable.name}) || to_tsvector('simple', ${gamesTable.slug}) @@ to_tsquery('simple', ${q})`)
// .orderBy(sql`${orderBy} ${order}`).offset(skip).limit(limit) || [];
if (foundGames.length === 0) {
error(404, { message: 'No games found' });
error(404, { message: 'No gamesTable found' })
}
console.log('Games found in Search API', JSON.stringify(foundGames, null, 2));
return json(foundGames);
console.log('Games found in Search API', JSON.stringify(foundGames, null, 2))
return json(foundGames)
}
};
}
type OrderDirection = 'asc' | 'desc';
type OrderDirection = 'asc' | 'desc'
const getOrderDirection = (direction: OrderDirection) => {
return direction === 'asc' ? asc : desc;
};
return direction === 'asc' ? asc : desc
}
const getOrderBy = (orderBy: string) => {
switch (orderBy) {
case 'name':
return games.name;
return games.name
case 'slug':
return games.slug;
return games.slug
default:
return games.slug;
return games.slug
}
};
}

View file

@ -1,19 +1,19 @@
import { getPublisher, updatePublisher } from '$lib/utils/db/publisherUtils.js';
import type { Publishers } from '$db/schema';
import type { Publishers } from '$db/schema'
import { getPublisher, updatePublisher } from '$lib/utils/db/publisherUtils.js'
export async function GET({ locals, params }) {
try {
return await getPublisher(locals, params.id);
return await getPublisher(locals, params.id)
} catch (e) {
console.error(e);
return new Response('Could not get publishers', {
console.error(e)
return new Response('Could not get publishersTable', {
status: 500,
});
})
}
}
export async function PUT({ locals, params, request }) {
const data: Publishers = await request.json();
const publisherId = params.id;
return await updatePublisher(locals, data, publisherId);
const data: Publishers = await request.json()
const publisherId = params.id
return await updatePublisher(locals, data, publisherId)
}

View file

@ -1,8 +0,0 @@
import { Hono } from 'hono'
import type { BlankSchema } from 'hono/types'
import type { HonoTypes } from '../../types'
export interface Controller {
controller: Hono<HonoTypes, BlankSchema, '/'>
routes(): any
}

View file

@ -1,5 +0,0 @@
import type { DatabaseProvider } from '$lib/server/api/providers/database.provider'
export interface Repository {
trxHost(trx: DatabaseProvider): any
}

View file

@ -0,0 +1,3 @@
export abstract class AsyncService {
async init(): Promise<void> {}
}

View file

@ -0,0 +1,26 @@
export interface Config {
isProduction: boolean
api: ApiConfig
storage: StorageConfig
redis: RedisConfig
postgres: PostgresConfig
}
interface ApiConfig {
origin: string
}
interface StorageConfig {
accessKey: string
secretKey: string
bucket: string
url: string
}
interface RedisConfig {
url: string
}
interface PostgresConfig {
url: string
}

View file

@ -0,0 +1,11 @@
import { Hono } from 'hono'
import type { BlankSchema } from 'hono/types'
import type { HonoTypes } from './hono'
export abstract class Controller {
protected readonly controller: Hono<HonoTypes, BlankSchema, '/'>
constructor() {
this.controller = new Hono()
}
abstract routes(): Hono<HonoTypes, BlankSchema, '/'>
}

View file

@ -1,16 +1,14 @@
import 'reflect-metadata'
import type { Controller } from '$lib/server/api/common/interfaces/controller.interface'
import { Controller } from '$lib/server/api/common/types/controller'
import { CollectionsService } from '$lib/server/api/services/collections.service'
import { Hono } from 'hono'
import { inject, injectable } from 'tsyringe'
import { requireAuth } from '../middleware/auth.middleware'
import type { HonoTypes } from '../types'
@injectable()
export class CollectionController implements Controller {
controller = new Hono<HonoTypes>()
constructor(@inject(CollectionsService) private readonly collectionsService: CollectionsService) {}
export class CollectionController extends Controller {
constructor(@inject(CollectionsService) private readonly collectionsService: CollectionsService) {
super()
}
routes() {
return this.controller

View file

@ -1,26 +1,24 @@
import { StatusCodes } from '$lib/constants/status-codes'
import type { Controller } from '$lib/server/api/common/interfaces/controller.interface'
import { Controller } from '$lib/server/api/common/types/controller'
import { updateEmailDto } from '$lib/server/api/dtos/update-email.dto'
import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto'
import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'
import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'
import { LuciaProvider } from '$lib/server/api/providers/lucia.provider'
import { IamService } from '$lib/server/api/services/iam.service'
import { LuciaService } from '$lib/server/api/services/lucia.service'
import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
import { setCookie } from 'hono/cookie'
import { inject, injectable } from 'tsyringe'
import { requireAuth } from '../middleware/auth.middleware'
import type { HonoTypes } from '../types'
@injectable()
export class IamController implements Controller {
controller = new Hono<HonoTypes>()
export class IamController extends Controller {
constructor(
@inject(IamService) private readonly iamService: IamService,
@inject(LuciaProvider) private lucia: LuciaProvider,
) {}
@inject(LuciaService) private luciaService: LuciaService,
) {
super()
}
routes() {
return this.controller
@ -59,7 +57,7 @@ export class IamController implements Controller {
.post('/logout', requireAuth, async (c) => {
const sessionId = c.var.session.id
await this.iamService.logout(sessionId)
const sessionCookie = this.lucia.createBlankSessionCookie()
const sessionCookie = this.luciaService.lucia.createBlankSessionCookie()
setCookie(c, sessionCookie.name, sessionCookie.value, {
path: sessionCookie.attributes.path,
maxAge: sessionCookie.attributes.maxAge,

View file

@ -1,30 +1,28 @@
import 'reflect-metadata'
import type { Controller } from '$lib/server/api/common/interfaces/controller.interface'
import { Controller } from '$lib/server/api/common/types/controller'
import { signinUsernameDto } from '$lib/server/api/dtos/signin-username.dto'
import { LuciaProvider } from '$lib/server/api/providers/lucia.provider'
import { LuciaService } from '$lib/server/api/services/lucia.service'
import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
import { setCookie } from 'hono/cookie'
import { TimeSpan } from 'oslo'
import { inject, injectable } from 'tsyringe'
import { limiter } from '../middleware/rate-limiter.middleware'
import { LoginRequestsService } from '../services/loginrequest.service'
import type { HonoTypes } from '../types'
@injectable()
export class LoginController implements Controller {
controller = new Hono<HonoTypes>()
export class LoginController extends Controller {
constructor(
@inject(LoginRequestsService) private readonly loginRequestsService: LoginRequestsService,
@inject(LuciaProvider) private lucia: LuciaProvider,
) {}
@inject(LuciaService) private luciaService: LuciaService,
) {
super()
}
routes() {
return this.controller.post('/', zValidator('json', signinUsernameDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
const { username, password } = c.req.valid('json')
const session = await this.loginRequestsService.verify({ username, password }, c.req)
const sessionCookie = this.lucia.createSessionCookie(session.id)
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id)
console.log('set cookie', sessionCookie)
setCookie(c, sessionCookie.name, sessionCookie.value, {
path: sessionCookie.attributes.path,

View file

@ -1,27 +1,24 @@
import 'reflect-metadata'
import { StatusCodes } from '$lib/constants/status-codes'
import type { Controller } from '$lib/server/api/common/interfaces/controller.interface'
import { Controller } from '$lib/server/api/common/types/controller'
import { verifyTotpDto } from '$lib/server/api/dtos/verify-totp.dto'
import { db } from '$lib/server/api/packages/drizzle'
import { RecoveryCodesService } from '$lib/server/api/services/recovery-codes.service'
import { TotpService } from '$lib/server/api/services/totp.service'
import { UsersService } from '$lib/server/api/services/users.service'
import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
import { inject, injectable } from 'tsyringe'
import { CredentialsType } from '../databases/tables'
import { requireAuth } from '../middleware/auth.middleware'
import type { HonoTypes } from '../types'
@injectable()
export class MfaController implements Controller {
controller = new Hono<HonoTypes>()
export class MfaController extends Controller {
constructor(
@inject(RecoveryCodesService) private readonly recoveryCodesService: RecoveryCodesService,
@inject(TotpService) private readonly totpService: TotpService,
@inject(UsersService) private readonly usersService: UsersService,
) {}
) {
super()
}
routes() {
return this.controller

View file

@ -1,26 +1,24 @@
import 'reflect-metadata'
import type { Controller } from '$lib/server/api/common/interfaces/controller.interface'
import { Controller } from '$lib/server/api/common/types/controller'
import { signupUsernameEmailDto } from '$lib/server/api/dtos/signup-username-email.dto'
import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'
import { LuciaProvider } from '$lib/server/api/providers/lucia.provider'
import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service'
import { LuciaService } from '$lib/server/api/services/lucia.service'
import { UsersService } from '$lib/server/api/services/users.service'
import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
import { setCookie } from 'hono/cookie'
import { TimeSpan } from 'oslo'
import { inject, injectable } from 'tsyringe'
import type { HonoTypes } from '../types'
@injectable()
export class SignupController implements Controller {
controller = new Hono<HonoTypes>()
export class SignupController extends Controller {
constructor(
@inject(UsersService) private readonly usersService: UsersService,
@inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService,
@inject(LuciaProvider) private lucia: LuciaProvider,
) {}
@inject(LuciaService) private luciaService: LuciaService,
) {
super()
}
routes() {
return this.controller.post('/', zValidator('json', signupUsernameEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
@ -38,7 +36,7 @@ export class SignupController implements Controller {
}
const session = await this.loginRequestService.createUserSession(user.id, c.req, undefined)
const sessionCookie = this.lucia.createSessionCookie(session.id)
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id)
console.log('set cookie', sessionCookie)
setCookie(c, sessionCookie.name, sessionCookie.value, {
path: sessionCookie.attributes.path,

View file

@ -1,16 +1,14 @@
import 'reflect-metadata'
import type { Controller } from '$lib/server/api/common/interfaces/controller.interface'
import { Controller } from '$lib/server/api/common/types/controller'
import { UsersService } from '$lib/server/api/services/users.service'
import { Hono } from 'hono'
import { inject, injectable } from 'tsyringe'
import { requireAuth } from '../middleware/auth.middleware'
import type { HonoTypes } from '../types'
@injectable()
export class UserController implements Controller {
controller = new Hono<HonoTypes>()
constructor(@inject(UsersService) private readonly usersService: UsersService) {}
export class UserController extends Controller {
constructor(@inject(UsersService) private readonly usersService: UsersService) {
super()
}
routes() {
return this.controller

View file

@ -1,16 +1,14 @@
import 'reflect-metadata'
import type { Controller } from '$lib/server/api/common/interfaces/controller.interface'
import { Controller } from '$lib/server/api/common/types/controller'
import { WishlistsService } from '$lib/server/api/services/wishlists.service'
import { Hono } from 'hono'
import { inject, injectable } from 'tsyringe'
import { requireAuth } from '../middleware/auth.middleware'
import type { HonoTypes } from '../types'
@injectable()
export class WishlistController implements Controller {
controller = new Hono<HonoTypes>()
constructor(@inject(WishlistsService) private readonly wishlistsService: WishlistsService) {}
export class WishlistController extends Controller {
constructor(@inject(WishlistsService) private readonly wishlistsService: WishlistsService) {
super()
}
routes() {
return this.controller

View file

@ -17,7 +17,11 @@ const connection = postgres({
const db = drizzle(connection)
try {
await migrate(db, { migrationsFolder: config.out! })
if (!config.out) {
console.error('No migrations folder specified in drizzle.config.ts')
process.exit()
}
await migrate(db, { migrationsFolder: config.out })
console.log('Migrations complete')
} catch (e) {
console.error(e)

View file

@ -47,6 +47,15 @@ CREATE TABLE IF NOT EXISTS "collections" (
CONSTRAINT "collections_cuid_unique" UNIQUE("cuid")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "credentials" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"type" text DEFAULT 'password' NOT NULL,
"secret_data" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "expansions" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"cuid" text,
@ -65,6 +74,16 @@ CREATE TABLE IF NOT EXISTS "external_ids" (
CONSTRAINT "external_ids_cuid_unique" UNIQUE("cuid")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "federated_identity" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"identity_provider" text NOT NULL,
"federated_user_id" text NOT NULL,
"federated_username" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "games" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"cuid" text,
@ -177,9 +196,9 @@ CREATE TABLE IF NOT EXISTS "sessions" (
CREATE TABLE IF NOT EXISTS "two_factor" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"cuid" text,
"two_factor_secret" text NOT NULL,
"two_factor_enabled" boolean DEFAULT false NOT NULL,
"initiated_time" timestamp with time zone NOT NULL,
"secret" text NOT NULL,
"enabled" boolean DEFAULT false NOT NULL,
"initiated_time" timestamp with time zone,
"user_id" uuid NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
@ -202,12 +221,12 @@ CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"cuid" text,
"username" text,
"hashed_password" text,
"email" text,
"first_name" text,
"last_name" text,
"verified" boolean DEFAULT false,
"receive_email" boolean DEFAULT false,
"mfa_enabled" boolean DEFAULT false NOT NULL,
"theme" text DEFAULT 'system',
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
@ -278,6 +297,12 @@ EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "credentials" ADD CONSTRAINT "credentials_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "expansions" ADD CONSTRAINT "expansions_base_game_id_games_id_fk" FOREIGN KEY ("base_game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
EXCEPTION
@ -290,6 +315,12 @@ EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "federated_identity" ADD CONSTRAINT "federated_identity_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "games_to_external_ids" ADD CONSTRAINT "games_to_external_ids_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
EXCEPTION

View file

@ -1,2 +0,0 @@
ALTER TABLE "two_factor" RENAME COLUMN "two_factor_secret" TO "secret";--> statement-breakpoint
ALTER TABLE "two_factor" RENAME COLUMN "two_factor_enabled" TO "enabled";

View file

@ -1 +0,0 @@
ALTER TABLE "two_factor" ALTER COLUMN "initiated_time" DROP NOT NULL;

View file

@ -1,32 +0,0 @@
CREATE TABLE IF NOT EXISTS "credentials" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"type" text DEFAULT 'password' NOT NULL,
"secret_data" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "federated_identity" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"identity_provider" text NOT NULL,
"federated_user_id" text NOT NULL,
"federated_username" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "credentials" ADD CONSTRAINT "credentials_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "federated_identity" ADD CONSTRAINT "federated_identity_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
ALTER TABLE "users" DROP COLUMN IF EXISTS "hashed_password";

View file

@ -1 +0,0 @@
ALTER TABLE "users" ADD COLUMN "enabled" boolean DEFAULT false NOT NULL;

View file

@ -1 +0,0 @@
ALTER TABLE "users" RENAME COLUMN "enabled" TO "mfa_enabled";

View file

@ -1,5 +1,5 @@
{
"id": "e120d11a-bf28-4c96-9f2f-96e23e23c7e2",
"id": "4760134e-48bb-47db-b431-56903dad6e24",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
@ -338,6 +338,70 @@
}
}
},
"public.credentials": {
"name": "credentials",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'password'"
},
"secret_data": {
"name": "secret_data",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"credentials_user_id_users_id_fk": {
"name": "credentials_user_id_users_id_fk",
"tableFrom": "credentials",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.expansions": {
"name": "expansions",
"schema": "",
@ -466,6 +530,75 @@
}
}
},
"public.federated_identity": {
"name": "federated_identity",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"identity_provider": {
"name": "identity_provider",
"type": "text",
"primaryKey": false,
"notNull": true
},
"federated_user_id": {
"name": "federated_user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"federated_username": {
"name": "federated_username",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"federated_identity_user_id_users_id_fk": {
"name": "federated_identity_user_id_users_id_fk",
"tableFrom": "federated_identity",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.games": {
"name": "games",
"schema": "",
@ -1273,14 +1406,14 @@
"primaryKey": false,
"notNull": false
},
"two_factor_secret": {
"name": "two_factor_secret",
"secret": {
"name": "secret",
"type": "text",
"primaryKey": false,
"notNull": true
},
"two_factor_enabled": {
"name": "two_factor_enabled",
"enabled": {
"name": "enabled",
"type": "boolean",
"primaryKey": false,
"notNull": true,
@ -1290,7 +1423,7 @@
"name": "initiated_time",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true
"notNull": false
},
"user_id": {
"name": "user_id",
@ -1461,12 +1594,6 @@
"primaryKey": false,
"notNull": false
},
"hashed_password": {
"name": "hashed_password",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
@ -1499,6 +1626,13 @@
"notNull": false,
"default": false
},
"mfa_enabled": {
"name": "mfa_enabled",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"theme": {
"name": "theme",
"type": "text",
@ -1720,6 +1854,7 @@
}
},
"schemas": {},
"sequences": {},
"_meta": {
"columns": {},
"schemas": {},

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,43 +5,8 @@
{
"idx": 0,
"version": "7",
"when": 1720625651245,
"tag": "0000_dazzling_stick",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1720625948784,
"tag": "0001_noisy_sally_floyd",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1720626020902,
"tag": "0002_fancy_valkyrie",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1723593488634,
"tag": "0003_worried_taskmaster",
"breakpoints": true
},
{
"idx": 4,
"version": "7",
"when": 1725055403926,
"tag": "0004_heavy_sphinx",
"breakpoints": true
},
{
"idx": 5,
"version": "7",
"when": 1725055643756,
"tag": "0005_true_mathemanic",
"when": 1725489682980,
"tag": "0000_volatile_warhawk",
"breakpoints": true
}
]

View file

@ -1,14 +1,18 @@
import { Table, getTableName, sql } from 'drizzle-orm'
import 'reflect-metadata'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type Table, getTableName, sql } from 'drizzle-orm'
import type { NodePgDatabase } from 'drizzle-orm/node-postgres'
import env from '../../../../env'
import { db, pool } from '../packages/drizzle'
import * as seeds from './seeds'
import * as schema from './tables'
const drizzleService = new DrizzleService()
if (!env.DB_SEEDING) {
throw new Error('You must set DB_SEEDING to "true" when running seeds')
}
async function resetTable(db: db, table: Table) {
async function resetTable(db: NodePgDatabase<typeof schema>, table: Table) {
return db.execute(sql.raw(`TRUNCATE TABLE ${getTableName(table)} RESTART IDENTITY CASCADE`))
}
@ -19,33 +23,33 @@ for (const table of [
schema.collection_items,
schema.collections,
schema.credentialsTable,
schema.expansions,
schema.externalIds,
schema.expansionsTable,
schema.externalIdsTable,
schema.federatedIdentityTable,
schema.games,
schema.gamesToExternalIds,
schema.mechanics,
schema.mechanicsToExternalIds,
schema.gamesTable,
schema.gamesToExternalIdsTable,
schema.mechanicsTable,
schema.mechanicsToExternalIdsTable,
schema.mechanics_to_games,
schema.password_reset_tokens,
schema.publishers,
schema.publishersToExternalIds,
schema.publishersTable,
schema.publishersToExternalIdsTable,
schema.publishers_to_games,
schema.recoveryCodesTable,
schema.roles,
schema.rolesTable,
schema.sessionsTable,
schema.twoFactorTable,
schema.user_roles,
schema.usersTable,
schema.wishlist_items,
schema.wishlists,
schema.wishlistsTable,
]) {
// await db.delete(table); // clear tables without truncating / resetting ids
await resetTable(db, table)
await resetTable(drizzleService.db, table)
}
await seeds.roles(db)
await seeds.users(db)
await seeds.roles(drizzleService.db)
await seeds.users(drizzleService.db)
await pool.end()
await drizzleService.dispose()
process.exit()

View file

@ -3,9 +3,9 @@ import { type db } from '$lib/server/api/packages/drizzle'
import roles from './data/roles.json'
export default async function seed(db: db) {
console.log('Creating roles ...')
console.log('Creating rolesTable ...')
for (const role of roles) {
await db.insert(schema.roles).values(role).onConflictDoNothing()
await db.insert(schema.rolesTable).values(role).onConflictDoNothing()
}
console.log('Roles created.')
}

View file

@ -1,29 +1,18 @@
import * as schema from '$lib/server/api/databases/tables'
import { type db } from '$lib/server/api/packages/drizzle'
import type { db } from '$lib/server/api/packages/drizzle'
import { eq } from 'drizzle-orm'
import { Argon2id } from 'oslo/password'
import { config } from '../../configs/config'
import users from './data/users.json'
type JsonUser = {
id: string
username: string
email: string
password: string
roles: {
name: string
primary: boolean
}[]
}
type JsonRole = {
name: string
primary: boolean
}
export default async function seed(db: db) {
const adminRole = await db.select().from(schema.roles).where(eq(schema.roles.name, 'admin'))
const userRole = await db.select().from(schema.roles).where(eq(schema.roles.name, 'user'))
const adminRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'admin'))
const userRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'user'))
console.log('Admin Role: ', adminRole)
const adminUser = await db
@ -48,7 +37,7 @@ export default async function seed(db: db) {
await db.insert(schema.collections).values({ user_id: adminUser[0].id }).onConflictDoNothing()
await db.insert(schema.wishlists).values({ user_id: adminUser[0].id }).onConflictDoNothing()
await db.insert(schema.wishlistsTable).values({ user_id: adminUser[0].id }).onConflictDoNothing()
await db
.insert(schema.user_roles)
@ -85,11 +74,11 @@ export default async function seed(db: db) {
secret_data: await new Argon2id().hash(user.password),
})
await db.insert(schema.collections).values({ user_id: insertedUser?.id })
await db.insert(schema.wishlists).values({ user_id: insertedUser?.id })
await db.insert(schema.wishlistsTable).values({ user_id: insertedUser?.id })
await Promise.all(
user.roles.map(async (role: JsonRole) => {
const foundRole = await db.query.roles.findFirst({
where: eq(schema.roles.name, role.name),
const foundRole = await db.query.rolesTable.findFirst({
where: eq(schema.rolesTable.name, role.name),
})
if (!foundRole) {
throw new Error('Role not found')

View file

@ -1,9 +1,9 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { categoriesToExternalIdsTable } from './categoriesToExternalIdsTable'
import { categories_to_games_table } from './categoriesToGames'
import { timestamps } from '../../common/utils/table'
import { categoriesToExternalIdsTable } from './categoriesToExternalIds.table'
import { categories_to_games_table } from './categoriesToGames.table'
export const categoriesTable = pgTable('categories', {
id: uuid('id').primaryKey().defaultRandom(),

View file

@ -0,0 +1,34 @@
import { relations } from 'drizzle-orm'
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { categoriesTable } from './categories.table'
import { externalIdsTable } from './externalIds.table'
export const categoriesToExternalIdsTable = pgTable(
'categories_to_external_ids',
{
categoryId: uuid('category_id')
.notNull()
.references(() => categoriesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id')
.notNull()
.references(() => externalIdsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
},
(table) => {
return {
categoriesToExternalIdsPkey: primaryKey({
columns: [table.categoryId, table.externalId],
}),
}
},
)
export const categoriesToExternalIdsRelations = relations(categoriesToExternalIdsTable, ({ one }) => ({
category: one(categoriesTable, {
fields: [categoriesToExternalIdsTable.categoryId],
references: [categoriesTable.id],
}),
externalId: one(externalIdsTable, {
fields: [categoriesToExternalIdsTable.externalId],
references: [externalIdsTable.id],
}),
}))

View file

@ -1,37 +0,0 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
import { categoriesTable } from './categories.table';
import { externalIds } from './externalIds';
import { relations } from 'drizzle-orm';
export const categoriesToExternalIdsTable = pgTable(
'categories_to_external_ids',
{
categoryId: uuid('category_id')
.notNull()
.references(() => categoriesTable.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 categoriesToExternalIdsRelations = relations(
categoriesToExternalIdsTable,
({ one }) => ({
category: one(categoriesTable, {
fields: [categoriesToExternalIdsTable.categoryId],
references: [categoriesTable.id],
}),
externalId: one(externalIds, {
fields: [categoriesToExternalIdsTable.externalId],
references: [externalIds.id],
}),
}),
);

View file

@ -1,7 +1,7 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
import { categoriesTable } from './categories.table';
import { games } from './games';
import { relations } from 'drizzle-orm'
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { gamesTable } from '././games.table'
import { categoriesTable } from './categories.table'
export const categories_to_games_table = pgTable(
'categories_to_games',
@ -11,25 +11,24 @@ export const categories_to_games_table = pgTable(
.references(() => categoriesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
game_id: uuid('game_id')
.notNull()
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
.references(() => gamesTable.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_table, ({ one }) => ({
category: one(categoriesTable, {
fields: [categories_to_games_table.category_id],
references: [categoriesTable.id],
}),
game: one(games, {
game: one(gamesTable, {
fields: [categories_to_games_table.game_id],
references: [games.id],
references: [gamesTable.id],
}),
}));
}))

View file

@ -1,9 +1,9 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { integer, pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { collections } from './collections'
import { games } from './games'
import { timestamps } from '../../common/utils/table'
import { gamesTable } from '././games.table'
import { collections } from './collections.table'
export const collection_items = pgTable('collection_items', {
id: uuid('id').primaryKey().defaultRandom(),
@ -15,20 +15,20 @@ export const collection_items = pgTable('collection_items', {
.references(() => collections.id, { onDelete: 'cascade' }),
game_id: uuid('game_id')
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
.references(() => gamesTable.id, { onDelete: 'cascade' }),
times_played: integer('times_played').default(0),
...timestamps,
})
export type CollectionItems = InferSelectModel<typeof collection_items>
export type CollectionItemsTable = 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, {
game: one(gamesTable, {
fields: [collection_items.game_id],
references: [games.id],
references: [gamesTable.id],
}),
}))

View file

@ -1,7 +1,7 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { usersTable } from './users.table'
export const collections = pgTable('collections', {

View file

@ -1,6 +1,6 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { type InferSelectModel } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { usersTable } from './users.table'
export enum CredentialsType {

View file

@ -0,0 +1,32 @@
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { gamesTable } from '././games.table'
export const expansionsTable = pgTable('expansions', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
.$defaultFn(() => cuid2()),
base_game_id: uuid('base_game_id')
.notNull()
.references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
game_id: uuid('game_id')
.notNull()
.references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
...timestamps,
})
export type Expansions = InferSelectModel<typeof expansionsTable>
export const expansion_relations = relations(expansionsTable, ({ one }) => ({
baseGame: one(gamesTable, {
fields: [expansionsTable.base_game_id],
references: [gamesTable.id],
}),
game: one(gamesTable, {
fields: [expansionsTable.game_id],
references: [gamesTable.id],
}),
}))

View file

@ -1,32 +0,0 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
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' }),
...timestamps,
})
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],
}),
}))

View file

@ -0,0 +1,16 @@
import { createId as cuid2 } from '@paralleldrive/cuid2'
import type { InferSelectModel } from 'drizzle-orm'
import { pgEnum, pgTable, text, uuid } from 'drizzle-orm/pg-core'
export const externalIdType = pgEnum('external_id_type', ['game', 'category', 'mechanic', 'publisher', 'designer', 'artist'])
export const externalIdsTable = pgTable('external_ids', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
.$defaultFn(() => cuid2()),
type: externalIdType('type'),
externalId: text('external_id').notNull(),
})
export type ExternalIds = InferSelectModel<typeof externalIdsTable>

View file

@ -1,23 +0,0 @@
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',
]);
export const externalIds = pgTable('external_ids', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
.$defaultFn(() => cuid2()),
type: externalIdType('type'),
externalId: text('external_id').notNull(),
});
export type ExternalIds = InferSelectModel<typeof externalIds>;

View file

@ -1,6 +1,6 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { type InferSelectModel } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { usersTable } from './users.table'
export const federatedIdentityTable = pgTable('federated_identity', {

View file

@ -1,13 +1,13 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations, sql } from 'drizzle-orm'
import { index, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
import { categories_to_games_table } from './categoriesToGames'
import { gamesToExternalIds } from './gamesToExternalIds'
import { mechanics_to_games } from './mechanicsToGames'
import { publishers_to_games } from './publishersToGames'
import { timestamps } from '../../common/utils/table'
import { categories_to_games_table } from './categoriesToGames.table'
import { gamesToExternalIdsTable } from './gamesToExternalIds.table'
import { mechanics_to_games } from './mechanicsToGames.table'
import { publishers_to_games } from './publishersToGames.table'
export const games = pgTable(
export const gamesTable = pgTable(
'games',
{
id: uuid('id').primaryKey().defaultRandom(),
@ -41,11 +41,11 @@ export const games = pgTable(
}),
)
export const gameRelations = relations(games, ({ many }) => ({
export const gameRelations = relations(gamesTable, ({ many }) => ({
categories_to_games: many(categories_to_games_table),
mechanics_to_games: many(mechanics_to_games),
publishers_to_games: many(publishers_to_games),
gamesToExternalIds: many(gamesToExternalIds),
gamesToExternalIds: many(gamesToExternalIdsTable),
}))
export type Games = InferSelectModel<typeof games>
export type Games = InferSelectModel<typeof gamesTable>

View file

@ -0,0 +1,22 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { gamesTable } from '././games.table'
import { externalIdsTable } from './externalIds.table'
export const gamesToExternalIdsTable = pgTable(
'games_to_external_ids',
{
gameId: uuid('game_id')
.notNull()
.references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id')
.notNull()
.references(() => externalIdsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
},
(table) => {
return {
gamesToExternalIdsPkey: primaryKey({
columns: [table.gameId, table.externalId],
}),
}
},
)

View file

@ -1,22 +0,0 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
import {games} from './games';
import {externalIds} from './externalIds';
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],
}),
};
},
);

View file

@ -1,26 +1,26 @@
export * from './categories.table';
export * from './categoriesToExternalIdsTable';
export * from './categoriesToGames';
export * from './collectionItems';
export * from './collections';
export * from './credentials.table';
export * from './expansions';
export * from './externalIds';
export * from './federatedIdentity.table';
export * from './games';
export * from './gamesToExternalIds';
export * from './mechanics';
export * from './mechanicsToExternalIds';
export * from './mechanicsToGames'
export * from './passwordResetTokens';
export * from './publishers';
export * from './publishersToExternalIds';
export * from './publishersToGames';
export * from './recovery-codes.table';
export * from './roles';
export * from './sessions.table';
export * from './two-factor.table';
export * from './userRoles';
export * from './users.table';
export * from './wishlistItems';
export * from './wishlists';
export * from './categories.table'
export * from './categoriesToExternalIds.table'
export * from './categoriesToGames.table'
export * from './collectionItems.table'
export * from './collections.table'
export * from './credentials.table'
export * from './expansions.table'
export * from './externalIds.table'
export * from './federatedIdentity.table'
export * from './games.table'
export * from './gamesToExternalIds.table'
export * from './mechanics.table'
export * from './mechanicsToExternalIds.table'
export * from './mechanicsToGames.table'
export * from './passwordResetTokens.table'
export * from './publishers.table'
export * from './publishersToExternalIds.table'
export * from './publishersToGames.table'
export * from './recovery-codes.table'
export * from './roles.table'
export * from './sessions.table'
export * from './two-factor.table'
export * from './userRoles.table'
export * from './users.table'
export * from './wishlistItems.table'
export * from './wishlists.table'

View file

@ -0,0 +1,23 @@
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { mechanicsToExternalIdsTable } from './mechanicsToExternalIds.table'
import { mechanics_to_games } from './mechanicsToGames.table'
export const mechanicsTable = pgTable('mechanics', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
.$defaultFn(() => cuid2()),
name: text('name'),
slug: text('slug'),
...timestamps,
})
export type Mechanics = InferSelectModel<typeof mechanicsTable>
export const mechanics_relations = relations(mechanicsTable, ({ many }) => ({
mechanics_to_games: many(mechanics_to_games),
mechanicsToExternalIds: many(mechanicsToExternalIdsTable),
}))

View file

@ -1,23 +0,0 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { mechanicsToExternalIds } from './mechanicsToExternalIds'
import { mechanics_to_games } from './mechanicsToGames'
export const mechanics = pgTable('mechanics', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
.$defaultFn(() => cuid2()),
name: text('name'),
slug: text('slug'),
...timestamps,
})
export type Mechanics = InferSelectModel<typeof mechanics>
export const mechanics_relations = relations(mechanics, ({ many }) => ({
mechanics_to_games: many(mechanics_to_games),
mechanicsToExternalIds: many(mechanicsToExternalIds),
}))

View file

@ -0,0 +1,22 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { externalIdsTable } from './externalIds.table'
import { mechanicsTable } from './mechanics.table'
export const mechanicsToExternalIdsTable = pgTable(
'mechanics_to_external_ids',
{
mechanicId: uuid('mechanic_id')
.notNull()
.references(() => mechanicsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id')
.notNull()
.references(() => externalIdsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
},
(table) => {
return {
mechanicsToExternalIdsPkey: primaryKey({
columns: [table.mechanicId, table.externalId],
}),
}
},
)

View file

@ -1,22 +0,0 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
import {mechanics} from './mechanics';
import {externalIds} from './externalIds';
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],
}),
};
},
);

View file

@ -0,0 +1,34 @@
import { relations } from 'drizzle-orm'
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { gamesTable } from '././games.table'
import { mechanicsTable } from './mechanics.table'
export const mechanics_to_games = pgTable(
'mechanics_to_games',
{
mechanic_id: uuid('mechanic_id')
.notNull()
.references(() => mechanicsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
game_id: uuid('game_id')
.notNull()
.references(() => gamesTable.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(mechanicsTable, {
fields: [mechanics_to_games.mechanic_id],
references: [mechanicsTable.id],
}),
game: one(gamesTable, {
fields: [mechanics_to_games.game_id],
references: [gamesTable.id],
}),
}))

View file

@ -1,34 +0,0 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
import {mechanics} from './mechanics';
import {games} from './games';
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],
}),
}));

View file

@ -1,7 +1,7 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { usersTable } from './users.table'
export const password_reset_tokens = pgTable('password_reset_tokens', {
@ -15,7 +15,7 @@ export const password_reset_tokens = pgTable('password_reset_tokens', {
...timestamps,
})
export type PasswordResetTokens = InferSelectModel<typeof password_reset_tokens>
export type PasswordResetTokensTable = InferSelectModel<typeof password_reset_tokens>
export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({
user: one(usersTable, {

View file

@ -0,0 +1,23 @@
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { publishersToExternalIdsTable } from './publishersToExternalIds.table'
import { publishers_to_games } from './publishersToGames.table'
export const publishersTable = pgTable('publishers', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
.$defaultFn(() => cuid2()),
name: text('name'),
slug: text('slug'),
...timestamps,
})
export type Publishers = InferSelectModel<typeof publishersTable>
export const publishers_relations = relations(publishersTable, ({ many }) => ({
publishers_to_games: many(publishers_to_games),
publishersToExternalIds: many(publishersToExternalIdsTable),
}))

View file

@ -1,23 +0,0 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { publishersToExternalIds } from './publishersToExternalIds'
import { publishers_to_games } from './publishersToGames'
export const publishers = pgTable('publishers', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
.$defaultFn(() => cuid2()),
name: text('name'),
slug: text('slug'),
...timestamps,
})
export type Publishers = InferSelectModel<typeof publishers>
export const publishers_relations = relations(publishers, ({ many }) => ({
publishers_to_games: many(publishers_to_games),
publishersToExternalIds: many(publishersToExternalIds),
}))

View file

@ -0,0 +1,22 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { externalIdsTable } from './externalIds.table'
import { publishersTable } from './publishers.table'
export const publishersToExternalIdsTable = pgTable(
'publishers_to_external_ids',
{
publisherId: uuid('publisher_id')
.notNull()
.references(() => publishersTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
externalId: uuid('external_id')
.notNull()
.references(() => externalIdsTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
},
(table) => {
return {
publishersToExternalIdsPkey: primaryKey({
columns: [table.publisherId, table.externalId],
}),
}
},
)

View file

@ -1,22 +0,0 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
import {publishers} from './publishers';
import {externalIds} from './externalIds';
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],
}),
};
},
);

View file

@ -1,34 +1,34 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
import {publishers} from './publishers';
import {games} from './games';
import { relations } from 'drizzle-orm'
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { gamesTable } from '././games.table'
import { publishersTable } from './publishers.table'
export const publishers_to_games = pgTable(
'publishers_to_games',
{
publisher_id: uuid('publisher_id')
.notNull()
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
.references(() => publishersTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
game_id: uuid('game_id')
.notNull()
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
.references(() => gamesTable.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, {
publisher: one(publishersTable, {
fields: [publishers_to_games.publisher_id],
references: [publishers.id],
references: [publishersTable.id],
}),
game: one(games, {
game: one(gamesTable, {
fields: [publishers_to_games.game_id],
references: [games.id],
references: [gamesTable.id],
}),
}));
}))

View file

@ -1,6 +1,6 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import type { InferSelectModel } from 'drizzle-orm'
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { usersTable } from './users.table'
export const recoveryCodesTable = pgTable('recovery_codes', {

View file

@ -1,10 +1,10 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { user_roles } from './userRoles'
import { timestamps } from '../../common/utils/table'
import { user_roles } from './userRoles.table'
export const roles = pgTable('roles', {
export const rolesTable = pgTable('roles', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
@ -14,8 +14,8 @@ export const roles = pgTable('roles', {
...timestamps,
})
export type Roles = InferSelectModel<typeof roles>
export type Roles = InferSelectModel<typeof rolesTable>
export const role_relations = relations(roles, ({ many }) => ({
export const role_relations = relations(rolesTable, ({ many }) => ({
user_roles: many(user_roles),
}))

View file

@ -1,7 +1,7 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { usersTable } from './users.table'
export const twoFactorTable = pgTable('two_factor', {

View file

@ -1,8 +1,8 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { roles } from './roles'
import { timestamps } from '../../common/utils/table'
import { rolesTable } from './roles.table'
import { usersTable } from './users.table'
export const user_roles = pgTable('user_roles', {
@ -15,15 +15,15 @@ export const user_roles = pgTable('user_roles', {
.references(() => usersTable.id, { onDelete: 'cascade' }),
role_id: uuid('role_id')
.notNull()
.references(() => roles.id, { onDelete: 'cascade' }),
.references(() => rolesTable.id, { onDelete: 'cascade' }),
primary: boolean('primary').default(false),
...timestamps,
})
export const user_role_relations = relations(user_roles, ({ one }) => ({
role: one(roles, {
role: one(rolesTable, {
fields: [user_roles.role_id],
references: [roles.id],
references: [rolesTable.id],
}),
user: one(usersTable, {
fields: [user_roles.user_id],
@ -31,4 +31,4 @@ export const user_role_relations = relations(user_roles, ({ one }) => ({
}),
}))
export type UserRoles = InferSelectModel<typeof user_roles>
export type UserRolesTable = InferSelectModel<typeof user_roles>

View file

@ -1,8 +1,8 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { user_roles } from './userRoles'
import { timestamps } from '../../common/utils/table'
import { user_roles } from './userRoles.table'
export const usersTable = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),

View file

@ -1,9 +1,9 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { games } from './games'
import { wishlists } from './wishlists'
import { timestamps } from '../../common/utils/table'
import { gamesTable } from '././games.table'
import { wishlistsTable } from './wishlists.table'
export const wishlist_items = pgTable('wishlist_items', {
id: uuid('id').primaryKey().defaultRandom(),
@ -12,22 +12,22 @@ export const wishlist_items = pgTable('wishlist_items', {
.$defaultFn(() => cuid2()),
wishlist_id: uuid('wishlist_id')
.notNull()
.references(() => wishlists.id, { onDelete: 'cascade' }),
.references(() => wishlistsTable.id, { onDelete: 'cascade' }),
game_id: uuid('game_id')
.notNull()
.references(() => games.id, { onDelete: 'cascade' }),
.references(() => gamesTable.id, { onDelete: 'cascade' }),
...timestamps,
})
export type WishlistItems = InferSelectModel<typeof wishlist_items>
export type WishlistItemsTable = InferSelectModel<typeof wishlist_items>
export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
wishlist: one(wishlists, {
wishlist: one(wishlistsTable, {
fields: [wishlist_items.wishlist_id],
references: [wishlists.id],
references: [wishlistsTable.id],
}),
game: one(games, {
game: one(gamesTable, {
fields: [wishlist_items.game_id],
references: [games.id],
references: [gamesTable.id],
}),
}))

View file

@ -1,10 +1,10 @@
import { timestamps } from '$lib/server/api/common/utils/table.utils'
import { createId as cuid2 } from '@paralleldrive/cuid2'
import { type InferSelectModel, relations } from 'drizzle-orm'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { timestamps } from '../../common/utils/table'
import { usersTable } from './users.table'
export const wishlists = pgTable('wishlists', {
export const wishlistsTable = pgTable('wishlists', {
id: uuid('id').primaryKey().defaultRandom(),
cuid: text('cuid')
.unique()
@ -16,11 +16,11 @@ export const wishlists = pgTable('wishlists', {
...timestamps,
})
export type Wishlists = InferSelectModel<typeof wishlists>
export type Wishlists = InferSelectModel<typeof wishlistsTable>
export const wishlists_relations = relations(wishlists, ({ one }) => ({
export const wishlists_relations = relations(wishlistsTable, ({ one }) => ({
user: one(usersTable, {
fields: [wishlists.user_id],
fields: [wishlistsTable.user_id],
references: [usersTable.id],
}),
}))

View file

@ -45,7 +45,7 @@ const routes = app
.route('/user', container.resolve(UserController).routes())
.route('/login', container.resolve(LoginController).routes())
.route('/signup', container.resolve(SignupController).routes())
.route('/wishlists', container.resolve(WishlistController).routes())
.route('/wishlistsTable', container.resolve(WishlistController).routes())
.route('/collections', container.resolve(CollectionController).routes())
.route('/mfa', container.resolve(MfaController).routes())
.get('/', (c) => c.json({ message: 'Server is healthy' }))

View file

@ -1,10 +1,14 @@
import { LuciaService } from '$lib/server/api/services/lucia.service'
import type { MiddlewareHandler } from 'hono'
import { createMiddleware } from 'hono/factory'
import type { Session, User } from 'lucia'
import { verifyRequestOrigin } from 'oslo/request'
import { container } from 'tsyringe'
import { Unauthorized } from '../common/exceptions'
import { lucia } from '../packages/lucia'
import type { HonoTypes } from '../types'
import type { HonoTypes } from '../common/types/hono'
// resolve dependencies from the container
const { lucia } = container.resolve(LuciaService)
export const verifyOrigin: MiddlewareHandler<HonoTypes> = createMiddleware(async (c, next) => {
if (c.req.method === 'GET') {
@ -27,7 +31,7 @@ export const validateAuthSession: MiddlewareHandler<HonoTypes> = createMiddlewar
}
const { session, user } = await lucia.validateSession(sessionId)
if (session && session.fresh) {
if (session?.fresh) {
c.header('Set-Cookie', lucia.createSessionCookie(session.id).serialize(), { append: true })
}
if (!session) {

View file

@ -1,10 +1,11 @@
import { rateLimiter } from 'hono-rate-limiter'
import RedisClient from 'ioredis'
import { RedisStore } from 'rate-limit-redis'
import { config } from '../configs/config'
import type { HonoTypes } from '../types'
import { container } from 'tsyringe'
import type { HonoTypes } from '../common/types/hono'
import { RedisService } from '../services/redis.service'
const client = new RedisClient(config.REDIS_URL)
// resolve dependencies from the container
const { client } = container.resolve(RedisService)
export function limiter({
limit,

View file

@ -0,0 +1,15 @@
import { Unauthorized } from '$lib/server/api/common/exceptions'
import type { MiddlewareHandler } from 'hono'
import { createMiddleware } from 'hono/factory'
import type { Session, User } from 'lucia'
export const requireAuth: MiddlewareHandler<{
Variables: {
session: Session
user: User
}
}> = createMiddleware(async (c, next) => {
const user = c.var.user
if (!user) throw Unauthorized('You must be logged in to access this resource')
return next()
})

View file

@ -1,63 +0,0 @@
import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle'
// lib/server/lucia.ts
import { Lucia, TimeSpan } from 'lucia'
import { config } from '../configs/config'
import { sessionsTable, usersTable } from '../databases/tables'
import { db } from './drizzle'
const adapter = new DrizzlePostgreSQLAdapter(db, sessionsTable, usersTable)
export const lucia = new Lucia(adapter, {
getSessionAttributes: (attributes) => {
return {
ipCountry: attributes.ip_country,
ipAddress: attributes.ip_address,
isTwoFactorAuthEnabled: attributes.twoFactorAuthEnabled,
isTwoFactorAuthenticated: attributes.isTwoFactorAuthenticated,
}
},
getUserAttributes: (attributes) => {
return {
// ...attributes,
username: attributes.username,
email: attributes.email,
firstName: attributes.first_name,
lastName: attributes.last_name,
mfa_enabled: attributes.mfa_enabled,
theme: attributes.theme,
}
},
sessionExpiresIn: new TimeSpan(2, 'w'), // 2 weeks
sessionCookie: {
name: 'session',
expires: false, // session cookies have very long lifespan (2 years)
attributes: {
// set to `true` when using HTTPS
secure: config.isProduction,
sameSite: 'strict',
domain: config.domain,
},
},
})
declare module 'lucia' {
interface Register {
Lucia: typeof lucia
DatabaseUserAttributes: DatabaseUserAttributes
DatabaseSessionAttributes: DatabaseSessionAttributes
}
interface DatabaseSessionAttributes {
ip_country: string
ip_address: string
twoFactorAuthEnabled: boolean
isTwoFactorAuthenticated: boolean
}
interface DatabaseUserAttributes {
username: string
email: string
first_name: string
last_name: string
mfa_enabled: boolean
theme: string
}
}

View file

@ -1,6 +1,5 @@
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository.utils'
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type InferInsertModel, eq } from 'drizzle-orm'
import { inject, injectable } from 'tsyringe'
import { collections } from '../databases/tables'
@ -9,15 +8,15 @@ export type CreateCollection = InferInsertModel<typeof collections>
export type UpdateCollection = Partial<CreateCollection>
@injectable()
export class CollectionsRepository implements Repository {
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
export class CollectionsRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
async findAll() {
return this.db.query.collections.findMany()
async findAll(db = this.drizzle.db) {
return db.query.collections.findMany()
}
async findOneById(id: string) {
return this.db.query.collections.findFirst({
async findOneById(id: string, db = this.drizzle.db) {
return db.query.collections.findFirst({
where: eq(collections.id, id),
columns: {
cuid: true,
@ -26,8 +25,8 @@ export class CollectionsRepository implements Repository {
})
}
async findOneByCuid(cuid: string) {
return this.db.query.collections.findFirst({
async findOneByCuid(cuid: string, db = this.drizzle.db) {
return db.query.collections.findFirst({
where: eq(collections.cuid, cuid),
columns: {
cuid: true,
@ -36,8 +35,8 @@ export class CollectionsRepository implements Repository {
})
}
async findOneByUserId(userId: string) {
return this.db.query.collections.findFirst({
async findOneByUserId(userId: string, db = this.drizzle.db) {
return db.query.collections.findFirst({
where: eq(collections.user_id, userId),
columns: {
cuid: true,
@ -46,21 +45,17 @@ export class CollectionsRepository implements Repository {
})
}
async findAllByUserId(userId: string) {
return this.db.query.collections.findMany({
async findAllByUserId(userId: string, db = this.drizzle.db) {
return db.query.collections.findMany({
where: eq(collections.user_id, userId),
})
}
async create(data: CreateCollection) {
return this.db.insert(collections).values(data).returning().then(takeFirstOrThrow)
async create(data: CreateCollection, db = this.drizzle.db) {
return db.insert(collections).values(data).returning().then(takeFirstOrThrow)
}
async update(id: string, data: UpdateCollection) {
return this.db.update(collections).set(data).where(eq(collections.id, id)).returning().then(takeFirstOrThrow)
}
trxHost(trx: DatabaseProvider) {
return new CollectionsRepository(trx)
async update(id: string, data: UpdateCollection, db = this.drizzle.db) {
return db.update(collections).set(data).where(eq(collections.id, id)).returning().then(takeFirstOrThrow)
}
}

View file

@ -1,75 +1,70 @@
import 'reflect-metadata'
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
import { CredentialsType, credentialsTable } from '$lib/server/api/databases/tables/credentials.table'
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type InferInsertModel, and, eq } from 'drizzle-orm'
import { inject, injectable } from 'tsyringe'
import { takeFirstOrThrow } from '../common/utils/repository.utils'
import { takeFirstOrThrow } from '../common/utils/repository'
export type CreateCredentials = InferInsertModel<typeof credentialsTable>
export type UpdateCredentials = Partial<CreateCredentials>
@injectable()
export class CredentialsRepository implements Repository {
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
export class CredentialsRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
async findOneByUserId(userId: string) {
return this.db.query.credentialsTable.findFirst({
async findOneByUserId(userId: string, db = this.drizzle.db) {
return db.query.credentialsTable.findFirst({
where: eq(credentialsTable.user_id, userId),
})
}
async findOneByUserIdAndType(userId: string, type: CredentialsType) {
return this.db.query.credentialsTable.findFirst({
async findOneByUserIdAndType(userId: string, type: CredentialsType, db = this.drizzle.db) {
return db.query.credentialsTable.findFirst({
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)),
})
}
async findPasswordCredentialsByUserId(userId: string) {
return this.db.query.credentialsTable.findFirst({
async findPasswordCredentialsByUserId(userId: string, db = this.drizzle.db) {
return db.query.credentialsTable.findFirst({
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.PASSWORD)),
})
}
async findTOTPCredentialsByUserId(userId: string) {
return this.db.query.credentialsTable.findFirst({
async findTOTPCredentialsByUserId(userId: string, db = this.drizzle.db) {
return db.query.credentialsTable.findFirst({
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.TOTP)),
})
}
async findOneById(id: string) {
return this.db.query.credentialsTable.findFirst({
async findOneById(id: string, db = this.drizzle.db) {
return db.query.credentialsTable.findFirst({
where: eq(credentialsTable.id, id),
})
}
async findOneByIdOrThrow(id: string) {
async findOneByIdOrThrow(id: string, db = this.drizzle.db) {
const credentials = await this.findOneById(id)
if (!credentials) throw Error('Credentials not found')
return credentials
}
async create(data: CreateCredentials) {
return this.db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow)
async create(data: CreateCredentials, db = this.drizzle.db) {
return db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow)
}
async update(id: string, data: UpdateCredentials) {
return this.db.update(credentialsTable).set(data).where(eq(credentialsTable.id, id)).returning().then(takeFirstOrThrow)
async update(id: string, data: UpdateCredentials, db = this.drizzle.db) {
return db.update(credentialsTable).set(data).where(eq(credentialsTable.id, id)).returning().then(takeFirstOrThrow)
}
async delete(id: string) {
return this.db.delete(credentialsTable).where(eq(credentialsTable.id, id))
async delete(id: string, db = this.drizzle.db) {
return db.delete(credentialsTable).where(eq(credentialsTable.id, id))
}
async deleteByUserId(userId: string) {
return this.db.delete(credentialsTable).where(eq(credentialsTable.user_id, userId))
async deleteByUserId(userId: string, db = this.drizzle.db) {
return db.delete(credentialsTable).where(eq(credentialsTable.user_id, userId))
}
async deleteByUserIdAndType(userId: string, type: CredentialsType) {
return this.db.delete(credentialsTable).where(and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)))
}
trxHost(trx: DatabaseProvider) {
return new CredentialsRepository(trx)
async deleteByUserIdAndType(userId: string, type: CredentialsType, db = this.drizzle.db) {
return db.delete(credentialsTable).where(and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)))
}
}

View file

@ -1,6 +1,5 @@
import 'reflect-metadata'
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type InferInsertModel, eq } from 'drizzle-orm'
import { inject, injectable } from 'tsyringe'
import { recoveryCodesTable } from '../databases/tables'
@ -8,20 +7,16 @@ import { recoveryCodesTable } from '../databases/tables'
export type CreateRecoveryCodes = InferInsertModel<typeof recoveryCodesTable>
@injectable()
export class RecoveryCodesRepository implements Repository {
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
export class RecoveryCodesRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
async findAllByUserId(userId: string) {
return this.db.query.recoveryCodesTable.findFirst({
async findAllByUserId(userId: string, db = this.drizzle.db) {
return db.query.recoveryCodesTable.findFirst({
where: eq(recoveryCodesTable.userId, userId),
})
}
async deleteAllByUserId(userId: string) {
return this.db.delete(recoveryCodesTable).where(eq(recoveryCodesTable.userId, userId))
}
trxHost(trx: DatabaseProvider) {
return new RecoveryCodesRepository(trx)
async deleteAllByUserId(userId: string, db = this.drizzle.db) {
return db.delete(recoveryCodesTable).where(eq(recoveryCodesTable.userId, userId))
}
}

View file

@ -1,9 +1,8 @@
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type InferInsertModel, eq } from 'drizzle-orm'
import { inject, injectable } from 'tsyringe'
import { takeFirstOrThrow } from '../common/utils/repository.utils'
import { roles } from '../databases/tables'
import { takeFirstOrThrow } from '../common/utils/repository'
import { rolesTable } from '../databases/tables'
/* -------------------------------------------------------------------------- */
/* Repository */
@ -21,54 +20,50 @@ storing data. They should not contain any business logic, only database queries.
In our case the method 'trxHost' is used to set the transaction context.
*/
export type CreateRole = InferInsertModel<typeof roles>
export type CreateRole = InferInsertModel<typeof rolesTable>
export type UpdateRole = Partial<CreateRole>
@injectable()
export class RolesRepository implements Repository {
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
export class RolesRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
async findOneById(id: string) {
return this.db.query.roles.findFirst({
where: eq(roles.id, id),
async findOneById(id: string, db = this.drizzle.db) {
return db.query.roles.findFirst({
where: eq(rolesTable.id, id),
})
}
async findOneByIdOrThrow(id: string) {
async findOneByIdOrThrow(id: string, db = this.drizzle.db) {
const role = await this.findOneById(id)
if (!role) throw Error('Role not found')
return role
}
async findAll() {
return this.db.query.roles.findMany()
async findAll(db = this.drizzle.db) {
return db.query.roles.findMany()
}
async findOneByName(name: string) {
return this.db.query.roles.findFirst({
where: eq(roles.name, name),
async findOneByName(name: string, db = this.drizzle.db) {
return db.query.roles.findFirst({
where: eq(rolesTable.name, name),
})
}
async findOneByNameOrThrow(name: string) {
async findOneByNameOrThrow(name: string, db = this.drizzle.db) {
const role = await this.findOneByName(name)
if (!role) throw Error('Role not found')
return role
}
async create(data: CreateRole) {
return this.db.insert(roles).values(data).returning().then(takeFirstOrThrow)
async create(data: CreateRole, db = this.drizzle.db) {
return db.insert(rolesTable).values(data).returning().then(takeFirstOrThrow)
}
async update(id: string, data: UpdateRole) {
return this.db.update(roles).set(data).where(eq(roles.id, id)).returning().then(takeFirstOrThrow)
async update(id: string, data: UpdateRole, db = this.drizzle.db) {
return db.update(rolesTable).set(data).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow)
}
async delete(id: string) {
return this.db.delete(roles).where(eq(roles.id, id)).returning().then(takeFirstOrThrow)
}
trxHost(trx: DatabaseProvider) {
return new RolesRepository(trx)
async delete(id: string, db = this.drizzle.db) {
return db.delete(rolesTable).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow)
}
}

View file

@ -1,8 +1,7 @@
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type InferInsertModel, eq } from 'drizzle-orm'
import { inject, injectable } from 'tsyringe'
import { takeFirstOrThrow } from '../common/utils/repository.utils'
import { takeFirstOrThrow } from '../common/utils/repository'
import { user_roles } from '../databases/tables'
/* -------------------------------------------------------------------------- */
@ -25,11 +24,11 @@ export type CreateUserRole = InferInsertModel<typeof user_roles>
export type UpdateUserRole = Partial<CreateUserRole>
@injectable()
export class UserRolesRepository implements Repository {
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
export class UserRolesRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
async findOneById(id: string) {
return this.db.query.user_roles.findFirst({
async findOneById(id: string, db = this.drizzle.db) {
return db.query.user_roles.findFirst({
where: eq(user_roles.id, id),
})
}
@ -40,21 +39,17 @@ export class UserRolesRepository implements Repository {
return userRole
}
async findAllByUserId(userId: string) {
return this.db.query.user_roles.findMany({
async findAllByUserId(userId: string, db = this.drizzle.db) {
return db.query.user_roles.findMany({
where: eq(user_roles.user_id, userId),
})
}
async create(data: CreateUserRole) {
return this.db.insert(user_roles).values(data).returning().then(takeFirstOrThrow)
async create(data: CreateUserRole, db = this.drizzle.db) {
return db.insert(user_roles).values(data).returning().then(takeFirstOrThrow)
}
async delete(id: string) {
return this.db.delete(user_roles).where(eq(user_roles.id, id)).returning().then(takeFirstOrThrow)
}
trxHost(trx: DatabaseProvider) {
return new UserRolesRepository(trx)
async delete(id: string, db = this.drizzle.db) {
return db.delete(user_roles).where(eq(user_roles.id, id)).returning().then(takeFirstOrThrow)
}
}

View file

@ -1,9 +1,8 @@
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
import { usersTable } from '$lib/server/api/databases/tables/users.table'
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type InferInsertModel, eq } from 'drizzle-orm'
import { inject, injectable } from 'tsyringe'
import { takeFirstOrThrow } from '../common/utils/repository.utils'
import { takeFirstOrThrow } from '../common/utils/repository'
/* -------------------------------------------------------------------------- */
/* Repository */
@ -25,46 +24,42 @@ export type CreateUser = InferInsertModel<typeof usersTable>
export type UpdateUser = Partial<CreateUser>
@injectable()
export class UsersRepository implements Repository {
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
export class UsersRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
async findOneById(id: string) {
return this.db.query.usersTable.findFirst({
async findOneById(id: string, db = this.drizzle.db) {
return db.query.usersTable.findFirst({
where: eq(usersTable.id, id),
})
}
async findOneByIdOrThrow(id: string) {
async findOneByIdOrThrow(id: string, db = this.drizzle.db) {
const user = await this.findOneById(id)
if (!user) throw Error('User not found')
return user
}
async findOneByUsername(username: string) {
return this.db.query.usersTable.findFirst({
async findOneByUsername(username: string, db = this.drizzle.db) {
return db.query.usersTable.findFirst({
where: eq(usersTable.username, username),
})
}
async findOneByEmail(email: string) {
return this.db.query.usersTable.findFirst({
async findOneByEmail(email: string, db = this.drizzle.db) {
return db.query.usersTable.findFirst({
where: eq(usersTable.email, email),
})
}
async create(data: CreateUser) {
return this.db.insert(usersTable).values(data).returning().then(takeFirstOrThrow)
async create(data: CreateUser, db = this.drizzle.db) {
return db.insert(usersTable).values(data).returning().then(takeFirstOrThrow)
}
async update(id: string, data: UpdateUser) {
return this.db.update(usersTable).set(data).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
async update(id: string, data: UpdateUser, db = this.drizzle.db) {
return db.update(usersTable).set(data).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
}
async delete(id: string) {
return this.db.delete(usersTable).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
}
trxHost(trx: DatabaseProvider) {
return new UsersRepository(trx)
async delete(id: string, db = this.drizzle.db) {
return db.delete(usersTable).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
}
}

View file

@ -1,24 +1,23 @@
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { type InferInsertModel, eq } from 'drizzle-orm'
import { inject, injectable } from 'tsyringe'
import { takeFirstOrThrow } from '../common/utils/repository.utils'
import { wishlists } from '../databases/tables'
import { takeFirstOrThrow } from '../common/utils/repository'
import { wishlistsTable } from '../databases/tables'
export type CreateWishlist = InferInsertModel<typeof wishlists>
export type CreateWishlist = InferInsertModel<typeof wishlistsTable>
export type UpdateWishlist = Partial<CreateWishlist>
@injectable()
export class WishlistsRepository implements Repository {
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
export class WishlistsRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
async findAll() {
return this.db.query.wishlists.findMany()
async findAll(db = this.drizzle.db) {
return db.query.wishlists.findMany()
}
async findOneById(id: string) {
return this.db.query.wishlists.findFirst({
where: eq(wishlists.id, id),
async findOneById(id: string, db = this.drizzle.db) {
return db.query.wishlists.findFirst({
where: eq(wishlistsTable.id, id),
columns: {
cuid: true,
name: true,
@ -26,9 +25,9 @@ export class WishlistsRepository implements Repository {
})
}
async findOneByCuid(cuid: string) {
return this.db.query.wishlists.findFirst({
where: eq(wishlists.cuid, cuid),
async findOneByCuid(cuid: string, db = this.drizzle.db) {
return db.query.wishlists.findFirst({
where: eq(wishlistsTable.cuid, cuid),
columns: {
cuid: true,
name: true,
@ -36,9 +35,9 @@ export class WishlistsRepository implements Repository {
})
}
async findOneByUserId(userId: string) {
return this.db.query.wishlists.findFirst({
where: eq(wishlists.user_id, userId),
async findOneByUserId(userId: string, db = this.drizzle.db) {
return db.query.wishlists.findFirst({
where: eq(wishlistsTable.user_id, userId),
columns: {
cuid: true,
name: true,
@ -46,9 +45,9 @@ export class WishlistsRepository implements Repository {
})
}
async findAllByUserId(userId: string) {
return this.db.query.wishlists.findMany({
where: eq(wishlists.user_id, userId),
async findAllByUserId(userId: string, db = this.drizzle.db) {
return db.query.wishlists.findMany({
where: eq(wishlistsTable.user_id, userId),
columns: {
cuid: true,
name: true,
@ -56,15 +55,11 @@ export class WishlistsRepository implements Repository {
})
}
async create(data: CreateWishlist) {
return this.db.insert(wishlists).values(data).returning().then(takeFirstOrThrow)
async create(data: CreateWishlist, db = this.drizzle.db) {
return db.insert(wishlistsTable).values(data).returning().then(takeFirstOrThrow)
}
async update(id: string, data: UpdateWishlist) {
return this.db.update(wishlists).set(data).where(eq(wishlists.id, id)).returning().then(takeFirstOrThrow)
}
trxHost(trx: DatabaseProvider) {
return new WishlistsRepository(trx)
async update(id: string, data: UpdateWishlist, db = this.drizzle.db) {
return db.update(wishlistsTable).set(data).where(eq(wishlistsTable.id, id)).returning().then(takeFirstOrThrow)
}
}

View file

@ -0,0 +1,33 @@
import { config } from '$lib/server/api/configs/config'
import * as schema from '$lib/server/api/databases/tables'
import { type NodePgDatabase, drizzle } from 'drizzle-orm/node-postgres'
import pg from 'pg'
import { type Disposable, injectable } from 'tsyringe'
@injectable()
export class DrizzleService implements Disposable {
protected readonly pool: pg.Pool
readonly db: NodePgDatabase<typeof schema>
readonly schema: typeof schema = schema
constructor() {
const pool = new pg.Pool({
user: config.DATABASE_USER,
password: config.DATABASE_PASSWORD,
host: config.DATABASE_HOST,
port: Number(config.DATABASE_PORT).valueOf(),
database: config.DATABASE_DB,
ssl: config.DATABASE_HOST !== 'localhost',
max: config.DB_MIGRATING || config.DB_SEEDING ? 1 : undefined,
})
this.pool = pool
this.db = drizzle(pool, {
schema,
logger: config.NODE_ENV === 'development',
})
}
dispose(): Promise<void> | void {
this.pool.end()
}
}

View file

@ -1,7 +1,7 @@
import type { UpdateEmailDto } from '$lib/server/api/dtos/update-email.dto'
import type { UpdateProfileDto } from '$lib/server/api/dtos/update-profile.dto'
import type { VerifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'
import { LuciaProvider } from '$lib/server/api/providers/lucia.provider'
import { LuciaService } from '$lib/server/api/services/lucia.service'
import { UsersService } from '$lib/server/api/services/users.service'
import { inject, injectable } from 'tsyringe'
@ -25,12 +25,12 @@ simple as possible. This makes the service easier to read, test and understand.
@injectable()
export class IamService {
constructor(
@inject(LuciaProvider) private readonly lucia: LuciaProvider,
@inject(LuciaService) private luciaService: LuciaService,
@inject(UsersService) private readonly usersService: UsersService,
) {}
async logout(sessionId: string) {
return this.lucia.invalidateSession(sessionId)
return this.luciaService.lucia.invalidateSession(sessionId)
}
async updateProfile(userId: string, data: UpdateProfileDto) {

View file

@ -1,10 +1,10 @@
import type { SigninUsernameDto } from '$lib/server/api/dtos/signin-username.dto'
import { LuciaService } from '$lib/server/api/services/lucia.service'
import type { HonoRequest } from 'hono'
import { inject, injectable } from 'tsyringe'
import { BadRequest } from '../common/exceptions'
import type { Credentials } from '../databases/tables'
import { DatabaseProvider } from '../providers/database.provider'
import { LuciaProvider } from '../providers/lucia.provider'
import { CredentialsRepository } from '../repositories/credentials.repository'
import { UsersRepository } from '../repositories/users.repository'
import { MailerService } from './mailer.service'
@ -13,7 +13,7 @@ import { TokensService } from './tokens.service'
@injectable()
export class LoginRequestsService {
constructor(
@inject(LuciaProvider) private readonly lucia: LuciaProvider,
@inject(LuciaService) private luciaService: LuciaService,
@inject(DatabaseProvider) private readonly db: DatabaseProvider,
@inject(TokensService) private readonly tokensService: TokensService,
@inject(MailerService) private readonly mailerService: MailerService,
@ -60,7 +60,7 @@ export class LoginRequestsService {
async createUserSession(existingUserId: string, req: HonoRequest, totpCredentials: Credentials | undefined) {
const requestIpAddress = req.header('x-real-ip')
const requestIpCountry = req.header('x-vercel-ip-country')
return this.lucia.createSession(existingUserId, {
return this.luciaService.lucia.createSession(existingUserId, {
ip_country: requestIpCountry || 'unknown',
ip_address: requestIpAddress || 'unknown',
twoFactorAuthEnabled: !!totpCredentials && totpCredentials?.secret_data !== null && totpCredentials?.secret_data !== '',

View file

@ -0,0 +1,45 @@
import { config } from '$lib/server/api/configs/config'
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle'
import { Lucia, TimeSpan } from 'lucia'
import { inject, injectable } from 'tsyringe'
@injectable()
export class LuciaService {
readonly lucia: Lucia
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {
const adapter = new DrizzlePostgreSQLAdapter(this.drizzle.db, this.drizzle.schema.sessionsTable, this.drizzle.schema.usersTable)
this.lucia = new Lucia(adapter, {
sessionExpiresIn: new TimeSpan(2, 'w'), // 2 weeks
sessionCookie: {
name: 'session',
expires: false, // session cookies have very long lifespan (2 years)
attributes: {
// set to `true` when using HTTPS
secure: config.isProduction,
sameSite: 'strict',
domain: config.domain,
},
},
getSessionAttributes: (attributes) => {
return {
ipCountry: attributes.ip_country,
ipAddress: attributes.ip_address,
isTwoFactorAuthEnabled: attributes.twoFactorAuthEnabled,
isTwoFactorAuthenticated: attributes.isTwoFactorAuthenticated,
}
},
getUserAttributes: (attributes) => {
return {
// ...attributes,
username: attributes.username,
email: attributes.email,
firstName: attributes.first_name,
lastName: attributes.last_name,
mfa_enabled: attributes.mfa_enabled,
theme: attributes.theme,
}
},
})
}
}

View file

@ -0,0 +1,18 @@
import { config } from '$lib/server/api/configs/config'
import { Redis } from 'ioredis'
import { type Disposable, injectable } from 'tsyringe'
@injectable()
export class RedisService implements Disposable {
readonly client: Redis
constructor() {
this.client = new Redis(config.REDIS_URL, {
maxRetriesPerRequest: null,
})
}
async dispose(): Promise<void> {
this.client.disconnect()
}
}

View file

@ -1,5 +1,5 @@
import { PUBLIC_SITE_URL } from '$env/static/public'
import { type Categories, type Mechanics, categoriesTable, categoriesToExternalIdsTable, externalIds } from '$lib/server/api/databases/tables'
import { type Categories, type Mechanics, categoriesTable, categoriesToExternalIdsTable, externalIdsTable } from '$lib/server/api/databases/tables'
import { db } from '$lib/server/api/packages/drizzle'
import { error } from '@sveltejs/kit'
import { eq } from 'drizzle-orm'
@ -12,7 +12,7 @@ export async function createCategory(locals: App.Locals, category: Categories, e
try {
const dbExternalId = await db.query.externalIds.findFirst({
where: eq(externalIds.externalId, externalId),
where: eq(externalIdsTable.externalId, externalId),
})
if (dbExternalId) {
@ -48,12 +48,12 @@ export async function createCategory(locals: App.Locals, category: Categories, e
})
.returning()
const dbExternalIds = await transaction
.insert(externalIds)
.insert(externalIdsTable)
.values({
externalId,
type: 'category',
})
.returning({ id: externalIds.id })
.returning({ id: externalIdsTable.id })
await transaction.insert(categoriesToExternalIdsTable).values({
categoryId: dbCategory[0].id,
externalId: dbExternalIds[0].id,

View file

@ -1,5 +1,5 @@
import { PUBLIC_SITE_URL } from '$env/static/public'
import { type Expansions, expansions } from '$lib/server/api/databases/tables'
import { type Expansions, expansionsTable } from '$lib/server/api/databases/tables'
import { db } from '$lib/server/api/packages/drizzle'
import { error } from '@sveltejs/kit'
import { and, eq } from 'drizzle-orm'
@ -11,7 +11,7 @@ export async function createExpansion(locals: App.Locals, expansion: Expansions)
try {
const foundExpansion = await db.query.expansions.findFirst({
where: and(eq(expansions.base_game_id, expansion.base_game_id), eq(expansions.game_id, expansion.game_id)),
where: and(eq(expansionsTable.base_game_id, expansion.base_game_id), eq(expansionsTable.game_id, expansion.game_id)),
columns: {
id: true,
game_id: true,
@ -32,7 +32,7 @@ export async function createExpansion(locals: App.Locals, expansion: Expansions)
console.log('Creating expansion', JSON.stringify(expansion, null, 2))
const dbExpansion = await db
.insert(expansions)
.insert(expansionsTable)
.values({
base_game_id: expansion.base_game_id,
game_id: expansion.game_id,
@ -146,7 +146,7 @@ export async function createExpansion(locals: App.Locals, expansion: Expansions)
// id: gameId
// },
// data: {
// expansions: {
// expansionsTable: {
// connect: {
// id: expansion.id
// }
@ -160,7 +160,7 @@ export async function createExpansion(locals: App.Locals, expansion: Expansions)
// id: baseGameId
// },
// data: {
// expansions: {
// expansionsTable: {
// connect: {
// id: expansion.id
// }

View file

@ -1,5 +1,5 @@
import { PUBLIC_SITE_URL } from '$env/static/public'
import { type Games, externalIds, games, gamesToExternalIds } from '$lib/server/api/databases/tables'
import { type Games, externalIdsTable, gamesTable, gamesToExternalIdsTable } from '$lib/server/api/databases/tables'
import { db } from '$lib/server/api/packages/drizzle'
import { error } from '@sveltejs/kit'
import { eq } from 'drizzle-orm'
@ -12,11 +12,11 @@ export async function getGame(locals: App.Locals, id: string) {
try {
return await db.query.games.findFirst({
where: eq(games.id, id),
where: eq(gamesTable.id, id),
})
} catch (e) {
console.error(e)
return new Response('Could not get games', {
return new Response('Could not get gamesTable', {
status: 500,
})
}
@ -29,18 +29,18 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
try {
const dbExternalId = await db.query.externalIds.findFirst({
where: eq(externalIds.externalId, externalId),
where: eq(externalIdsTable.externalId, externalId),
})
if (dbExternalId) {
const foundGame = await db
.select({
id: games.id,
name: games.name,
slug: games.slug,
id: gamesTable.id,
name: gamesTable.name,
slug: gamesTable.slug,
})
.from(games)
.leftJoin(gamesToExternalIds, eq(gamesToExternalIds.externalId, externalId))
.from(gamesTable)
.leftJoin(gamesToExternalIdsTable, eq(gamesToExternalIdsTable.externalId, externalId))
console.log('Game already exists', foundGame)
if (foundGame.length > 0) {
console.log('Game name', foundGame[0].name)
@ -58,7 +58,7 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
console.log('Creating game', JSON.stringify(game, null, 2))
await db.transaction(async (transaction) => {
dbGames = await transaction
.insert(games)
.insert(gamesTable)
.values({
name: game.name,
slug: kebabCase(game.name || game.slug || ''),
@ -75,13 +75,13 @@ export async function createGame(locals: App.Locals, game: Games, externalId: st
})
.returning()
const dbExternalIds = await transaction
.insert(externalIds)
.insert(externalIdsTable)
.values({
externalId,
type: 'game',
})
.returning({ id: externalIds.id })
await transaction.insert(gamesToExternalIds).values({
.returning({ id: externalIdsTable.id })
await transaction.insert(gamesToExternalIdsTable).values({
gameId: dbGames[0].id,
externalId: dbExternalIds[0].id,
})
@ -115,7 +115,7 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games,
console.log('Creating game', JSON.stringify(game, null, 2))
await db.transaction(async (transaction) => {
dbGames = await transaction
.insert(games)
.insert(gamesTable)
.values({
name: game.name,
slug: kebabCase(game.name ?? game.slug ?? ''),
@ -131,7 +131,7 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games,
max_playtime: game.max_playtime,
})
.onConflictDoUpdate({
target: games.id,
target: gamesTable.id,
set: {
name: game.name,
slug: kebabCase(game.name || game.slug || ''),
@ -149,15 +149,15 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games,
})
.returning()
const dbExternalIds = await transaction
.insert(externalIds)
.insert(externalIdsTable)
.values({
externalId,
type: 'game',
})
.onConflictDoNothing()
.returning({ id: externalIds.id })
.returning({ id: externalIdsTable.id })
await transaction
.insert(gamesToExternalIds)
.insert(gamesToExternalIdsTable)
.values({
gameId: dbGames[0].id,
externalId: dbExternalIds[0].id,
@ -189,18 +189,18 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
try {
const externalUrl = `https://boardgamegeek.com/boardgame/${externalId}`
const dbExternalId = await db.query.externalIds.findFirst({
where: eq(externalIds.externalId, externalId),
where: eq(externalIdsTable.externalId, externalId),
})
if (dbExternalId) {
const foundGame = await db
.select({
id: games.id,
name: games.name,
slug: games.slug,
id: gamesTable.id,
name: gamesTable.name,
slug: gamesTable.slug,
})
.from(games)
.leftJoin(gamesToExternalIds, eq(gamesToExternalIds.externalId, externalId))
.from(gamesTable)
.leftJoin(gamesToExternalIdsTable, eq(gamesToExternalIdsTable.externalId, externalId))
console.log('Game already exists', foundGame)
if (foundGame.length > 0) {
console.log('Game name', foundGame[0].name)
@ -218,7 +218,7 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
console.log('Creating game', JSON.stringify(game, null, 2))
await db.transaction(async (transaction) => {
dbGames = await transaction
.insert(games)
.insert(gamesTable)
.values({
name: game.name,
slug: kebabCase(game.name || game.slug || ''),
@ -234,7 +234,7 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
max_playtime: game.max_playtime,
})
.onConflictDoUpdate({
target: games.id,
target: gamesTable.id,
set: {
name: game.name,
slug: kebabCase(game.name || game.slug || ''),
@ -252,15 +252,15 @@ export async function createOrUpdateGame(locals: App.Locals, game: Games, extern
})
.returning()
const dbExternalIds = await transaction
.insert(externalIds)
.insert(externalIdsTable)
.values({
externalId,
type: 'game',
})
.onConflictDoNothing()
.returning({ id: externalIds.id })
.returning({ id: externalIdsTable.id })
await transaction
.insert(gamesToExternalIds)
.insert(gamesToExternalIdsTable)
.values({
gameId: dbGames[0].id,
externalId: dbExternalIds[0].id,
@ -291,7 +291,7 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
try {
const dbGame = await db
.update(games)
.update(gamesTable)
.set({
name: game.name,
slug: kebabCase(game.name || game.slug || ''),
@ -306,7 +306,7 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
min_playtime: game.min_playtime,
max_playtime: game.max_playtime,
})
.where(eq(games.id, id))
.where(eq(gamesTable.id, id))
.returning()
return new Response(JSON.stringify(dbGame[0]), {
headers: {
@ -315,7 +315,7 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
})
} catch (e) {
console.error(e)
return new Response('Could not get publishers', {
return new Response('Could not get publishersTable', {
status: 500,
})
}
@ -323,17 +323,17 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
// console.log('Creating or updating game', JSON.stringify(game, null, 2));
// const categoryIds = game.categories;
// const mechanicIds = game.mechanics;
// const publisherIds = game.publishers;
// const mechanicIds = game.mechanicsTable;
// const publisherIds = game.publishersTable;
// const designerIds = game.designers;
// const artistIds = game.artists;
// // const expansionIds = game.expansions;
// // const expansionIds = game.expansionsTable;
// const externalUrl = `https://boardgamegeek.com/boardgame/${game.external_id}`;
// console.log('categoryIds', categoryIds);
// console.log('mechanicIds', mechanicIds);
// await db.transaction(async (transaction) => {
// const dbGame = await db.transaction(async (transaction) => {
// transaction.insert(games).values({
// transaction.insert(gamesTable).values({
// name: game.name,
// slug: kebabCase(game.name || ''),
// description: game.description,
@ -349,7 +349,7 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
// year_published: game.year_published || 0,
// last_sync_at: new Date(),
// }).onConflictDoUpdate({
// target: games.id, set: {
// target: gamesTable.id, set: {
// name: game.name,
// slug: kebabCase(game.name),
// description: game.description,
@ -369,13 +369,13 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
// });
// // TODO: Connect to everything else
// });
// await db.insert(games).values({
// await db.insert(gamesTable).values({
// include: {
// mechanics: true,
// publishers: true,
// mechanicsTable: true,
// publishersTable: true,
// designers: true,
// artists: true,
// expansions: true
// expansionsTable: true
// },
// where: {
// external_id: game.external_id
@ -398,10 +398,10 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
// categories: {
// connect: categoryIds
// },
// mechanics: {
// mechanicsTable: {
// connect: mechanicIds
// },
// publishers: {
// publishersTable: {
// connect: publisherIds
// },
// designers: {
@ -429,10 +429,10 @@ export async function updateGame(locals: App.Locals, game: Games, id: string) {
// categories: {
// connect: categoryIds
// },
// mechanics: {
// mechanicsTable: {
// connect: mechanicIds
// },
// publishers: {
// publishersTable: {
// connect: publisherIds
// },
// designers: {

View file

@ -1,5 +1,5 @@
import { PUBLIC_SITE_URL } from '$env/static/public'
import { type Mechanics, externalIds, mechanics, mechanicsToExternalIds } from '$lib/server/api/databases/tables'
import { type Mechanics, externalIdsTable, mechanicsTable, mechanicsToExternalIdsTable } from '$lib/server/api/databases/tables'
import { db } from '$lib/server/api/packages/drizzle'
import { error } from '@sveltejs/kit'
import { eq } from 'drizzle-orm'
@ -12,18 +12,18 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex
try {
const dbExternalId = await db.query.externalIds.findFirst({
where: eq(externalIds.externalId, externalId),
where: eq(externalIdsTable.externalId, externalId),
})
if (dbExternalId) {
const foundMechanic = await db
.select({
id: mechanics.id,
name: mechanics.name,
slug: mechanics.slug,
id: mechanicsTable.id,
name: mechanicsTable.name,
slug: mechanicsTable.slug,
})
.from(mechanics)
.leftJoin(mechanicsToExternalIds, eq(mechanicsToExternalIds.externalId, externalId))
.from(mechanicsTable)
.leftJoin(mechanicsToExternalIdsTable, eq(mechanicsToExternalIdsTable.externalId, externalId))
console.log('Mechanic already exists', foundMechanic)
if (foundMechanic.length > 0) {
console.log('Mechanic name', foundMechanic[0].name)
@ -41,20 +41,20 @@ export async function createMechanic(locals: App.Locals, mechanic: Mechanics, ex
console.log('Creating mechanic', JSON.stringify(mechanic, null, 2))
await db.transaction(async (transaction) => {
dbMechanics = await transaction
.insert(mechanics)
.insert(mechanicsTable)
.values({
name: mechanic.name,
slug: kebabCase(mechanic.name || mechanic.slug || ''),
})
.returning()
const dbExternalIds = await transaction
.insert(externalIds)
.insert(externalIdsTable)
.values({
externalId,
type: 'mechanic',
})
.returning({ id: externalIds.id })
await transaction.insert(mechanicsToExternalIds).values({
.returning({ id: externalIdsTable.id })
await transaction.insert(mechanicsToExternalIdsTable).values({
mechanicId: dbMechanics[0].id,
externalId: dbExternalIds[0].id,
})

View file

@ -1,12 +1,12 @@
import { PUBLIC_SITE_URL } from '$env/static/public'
import { type Publishers, externalIds, publishers, publishersToExternalIds } from '$lib/server/api/databases/tables'
import { type Publishers, externalIdsTable, publishersTable, publishersToExternalIdsTable } from '$lib/server/api/databases/tables'
import { db } from '$lib/server/api/packages/drizzle'
import { error } from '@sveltejs/kit'
import { eq } from 'drizzle-orm'
import kebabCase from 'just-kebab-case'
export async function getPublisher(locals: App.Locals, id: string) {
const publisher = await db.select().from(publishers).where(eq(publishers.id, id))
const publisher = await db.select().from(publishersTable).where(eq(publishersTable.id, id))
if (publisher.length === 0) {
error(404, 'not found')
}
@ -24,12 +24,12 @@ export async function updatePublisher(locals: App.Locals, publisher: Publishers,
try {
const dbPublisher = await db
.update(publishers)
.update(publishersTable)
.set({
name: publisher.name,
slug: kebabCase(publisher.name || ''),
})
.where(eq(publishers.id, id))
.where(eq(publishersTable.id, id))
.returning()
return new Response(JSON.stringify(dbPublisher[0]), {
headers: {
@ -38,7 +38,7 @@ export async function updatePublisher(locals: App.Locals, publisher: Publishers,
})
} catch (e) {
console.error(e)
return new Response('Could not get publishers', {
return new Response('Could not get publishersTable', {
status: 500,
})
}
@ -51,18 +51,18 @@ export async function createPublisher(locals: App.Locals, publisher: Publishers,
try {
const dbExternalId = await db.query.externalIds.findFirst({
where: eq(externalIds.externalId, externalId),
where: eq(externalIdsTable.externalId, externalId),
})
if (dbExternalId) {
const foundPublisher = await db
.select({
id: publishers.id,
name: publishers.name,
slug: publishers.slug,
id: publishersTable.id,
name: publishersTable.name,
slug: publishersTable.slug,
})
.from(publishers)
.leftJoin(publishersToExternalIds, eq(publishersToExternalIds.externalId, externalId))
.from(publishersTable)
.leftJoin(publishersToExternalIdsTable, eq(publishersToExternalIdsTable.externalId, externalId))
console.log('Publisher already exists', foundPublisher)
if (foundPublisher.length > 0) {
console.log('Publisher name', foundPublisher[0].name)
@ -80,20 +80,20 @@ export async function createPublisher(locals: App.Locals, publisher: Publishers,
console.log('Creating publisher', JSON.stringify(publisher, null, 2))
await db.transaction(async (transaction) => {
dbPublishers = await transaction
.insert(publishers)
.insert(publishersTable)
.values({
name: publisher.name,
slug: kebabCase(publisher.name || publisher.slug || ''),
})
.returning()
const dbExternalIds = await transaction
.insert(externalIds)
.insert(externalIdsTable)
.values({
externalId,
type: 'publisher',
})
.returning({ id: externalIds.id })
await transaction.insert(publishersToExternalIds).values({
.returning({ id: externalIdsTable.id })
await transaction.insert(publishersToExternalIdsTable).values({
publisherId: dbPublishers[0].id,
externalId: dbExternalIds[0].id,
})

View file

@ -1,5 +1,5 @@
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages'
import { roles, user_roles, usersTable } from '$lib/server/api/databases/tables'
import { rolesTable, user_roles, usersTable } from '$lib/server/api/databases/tables'
import { db } from '$lib/server/api/packages/drizzle'
import { and, eq, inArray, not } from 'drizzle-orm'
import { redirect } from 'sveltekit-flash-message/server'
@ -40,7 +40,7 @@ export const load: PageServerLoad = async (event) => {
let availableRoles: { name: string; cuid: string }[] = []
if (currentRoleIds?.length > 0) {
availableRoles = await db.query.roles.findMany({
where: not(inArray(roles.cuid, currentRoleIds)),
where: not(inArray(rolesTable.cuid, currentRoleIds)),
columns: {
name: true,
cuid: true,
@ -86,7 +86,7 @@ export const actions = {
const data = await request.formData()
const role = data.get('role')
const dbRole = await db.query.roles.findFirst({
where: eq(roles.cuid, role?.toString() ?? ''),
where: eq(rolesTable.cuid, role?.toString() ?? ''),
})
console.log('dbRole', dbRole)
if (dbRole) {
@ -126,7 +126,7 @@ export const actions = {
const data = await request.formData()
const role = data.get('role')
const dbRole = await db.query.roles.findFirst({
where: eq(roles.cuid, role?.toString() ?? ''),
where: eq(rolesTable.cuid, role?.toString() ?? ''),
})
console.log('dbRole', dbRole)
if (dbRole) {

View file

@ -1,13 +1,13 @@
<script lang="ts">
import { enhance } from '$app/forms';
import capitalize from 'just-capitalize';
import { Button } from '$lib/components/ui/button';
// import AddRolesForm from './add-roles-form.svelte';
import { enhance } from '$app/forms'
import { Button } from '$lib/components/ui/button'
import capitalize from 'just-capitalize'
// import AddRolesForm from './add-rolesTable-form.svelte';
const { data } = $props();
const { data } = $props()
const { user, availableRoles } = data;
const { user_roles }: { user_roles: { role: { name: string, cuid: string } }[] } = user;
const { user, availableRoles } = data
const { user_roles }: { user_roles: { role: { name: string; cuid: string } }[] } = user
</script>
<h1>User Details</h1>

View file

@ -1,38 +1,34 @@
<script lang="ts">
import * as Form from '$lib/components/ui/form';
import { Input } from '$lib/components/ui/input';
import { Checkbox } from '$lib/components/ui/checkbox/index.js';
import { addRoleSchema, type AddRoleSchema } from '$lib/validations/account';
import {
type SuperValidated,
type Infer,
superForm
} from 'sveltekit-superforms';
import { zodClient } from 'sveltekit-superforms/adapters';
import { Checkbox } from '$lib/components/ui/checkbox/index.js'
import * as Form from '$lib/components/ui/form'
import { Input } from '$lib/components/ui/input'
import { type AddRoleSchema, addRoleSchema } from '$lib/validations/account'
import { type Infer, type SuperValidated, superForm } from 'sveltekit-superforms'
import { zodClient } from 'sveltekit-superforms/adapters'
export let availableRoles: { name: string; cuid: string }[] = [];
const data: SuperValidated<Infer<AddRoleSchema>> = availableRoles;
export let availableRoles: { name: string; cuid: string }[] = []
const data: SuperValidated<Infer<AddRoleSchema>> = availableRoles
const form = superForm(data, {
validators: zodClient(addRoleSchema)
// onUpdated: ({ form: f }) => {
// if (f.valid) {
// toast.success("You submitted" + JSON.stringify(f.data, null, 2));
// } else {
// toast.error("Please fix the errors in the form.");
// }
// }
});
const form = superForm(data, {
validators: zodClient(addRoleSchema),
// onUpdated: ({ form: f }) => {
// if (f.valid) {
// toast.success("You submitted" + JSON.stringify(f.data, null, 2));
// } else {
// toast.error("Please fix the errors in the form.");
// }
// }
})
const { form: formData, enhance } = form;
const { form: formData, enhance } = form
function addRole(id: string) {
$formData.roles = [...$formData.roles, id];
}
function addRole(id: string) {
$formData.roles = [...$formData.roles, id]
}
function removeRole(id: string) {
$formData.roles = $formData.roles.filter((i) => i !== id);
}
function removeRole(id: string) {
$formData.roles = $formData.roles.filter((i) => i !== id)
}
</script>
<form action="/?/addMultipleRoles" method="POST" use:enhance>
@ -40,7 +36,7 @@
<div class="mb-4">
<Form.Legend class="text-base">Roles</Form.Legend>
<Form.Description>
Select the roles you want to add to the user.
Select the rolesTable you want to add to the user.
</Form.Description>
</div>
<div class="space-y-2">

View file

@ -7,7 +7,7 @@ import { and, eq } from 'drizzle-orm'
import { redirect } from 'sveltekit-flash-message/server'
import { zod } from 'sveltekit-superforms/adapters'
import { superValidate } from 'sveltekit-superforms/server'
import { collection_items, collections, games } from '../../../../lib/server/api/databases/tables'
import { collection_items, collections, gamesTable } from '../../../../lib/server/api/databases/tables'
export async function load(event) {
const { user, session } = event.locals
@ -54,7 +54,7 @@ export const actions: Actions = {
const user = event.locals.user
const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id),
where: eq(gamesTable.id, form.data.id),
})
if (!game) {
@ -115,7 +115,7 @@ export const actions: Actions = {
}
const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id),
where: eq(gamesTable.id, form.data.id),
})
if (!game) {

View file

@ -7,7 +7,7 @@ import { and, eq } from 'drizzle-orm'
import { redirect } from 'sveltekit-flash-message/server'
import { zod } from 'sveltekit-superforms/adapters'
import { superValidate } from 'sveltekit-superforms/server'
import { collection_items, collections, games } from '../../../../../lib/server/api/databases/tables'
import { collection_items, collections, gamesTable } from '../../../../../lib/server/api/databases/tables'
export async function load(event) {
const { params, locals } = event
@ -139,7 +139,7 @@ export const actions: Actions = {
const form = await superValidate(event, zod(modifyListGameSchema))
const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id),
where: eq(gamesTable.id, form.data.id),
})
if (!game) {
@ -204,7 +204,7 @@ export const actions: Actions = {
const form = await superValidate(event, zod(modifyListGameSchema))
const game = await db.query.games.findFirst({
where: eq(games.id, form.data.id),
where: eq(gamesTable.id, form.data.id),
})
if (!game) {

Some files were not shown because too many files have changed in this diff Show more