mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Merge pull request #24 from BradNut/hono
Refactoring to match SvelteTofuStack and additional updates
This commit is contained in:
commit
ff017a85b3
163 changed files with 3302 additions and 12329 deletions
30
.env.example
Normal file
30
.env.example
Normal 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
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||||
"style": "default",
|
"style": "default",
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "tailwind.config.js",
|
"config": "tailwind.config.js",
|
||||||
"css": "src/app.postcss",
|
"css": "src/lib/styles/app.pcss",
|
||||||
"baseColor": "slate"
|
"baseColor": "slate"
|
||||||
},
|
},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"components": "$lib/components",
|
"components": "$lib/components",
|
||||||
"utils": "$lib/utils"
|
"utils": "$lib/utils/ui"
|
||||||
},
|
},
|
||||||
"typescript": true
|
"typescript": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import 'dotenv/config'
|
import 'dotenv/config'
|
||||||
|
import env from '$lib/server/api/common/env'
|
||||||
import { defineConfig } from 'drizzle-kit'
|
import { defineConfig } from 'drizzle-kit'
|
||||||
import env from './src/env'
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: 'postgresql',
|
dialect: 'postgresql',
|
||||||
out: './src/lib/server/api/databases/migrations',
|
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: {
|
dbCredentials: {
|
||||||
host: env.DATABASE_HOST || 'localhost',
|
host: env.DATABASE_HOST || 'localhost',
|
||||||
port: Number(env.DATABASE_PORT) || 5432,
|
port: Number(env.DATABASE_PORT) || 5432,
|
||||||
|
|
|
||||||
|
|
@ -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 }) => {
|
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
|
// TODO: Debounce excessive calls and possibly throttle
|
||||||
if (isNaN(game_id) || !isFinite(game_id)) {
|
if (isNaN(game_id) || !isFinite(game_id)) {
|
||||||
error(400, { message: 'Invalid game id' });
|
error(400, { message: 'Invalid game id' })
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return json(await getGame(locals, params.id));
|
return json(await getGame(locals, params.id))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
return new Response('Could not get games', {
|
return new Response('Could not get gamesTable', {
|
||||||
status: 500
|
status: 500,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,34 @@
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { games } from '$db/schema'
|
||||||
import db from '../../../../db';
|
import { FilterSchema, PaginationSchema, SearchSchema, SortSchema } from '$lib/validations/zod-schemas'
|
||||||
import { asc, desc, eq, ilike, or } from 'drizzle-orm';
|
import { error, json } from '@sveltejs/kit'
|
||||||
import { games } from '$db/schema';
|
import { asc, desc, eq, ilike, or } from 'drizzle-orm'
|
||||||
import kebabCase from 'just-kebab-case';
|
import kebabCase from 'just-kebab-case'
|
||||||
import {
|
import db from '../../../../db'
|
||||||
FilterSchema,
|
|
||||||
PaginationSchema,
|
|
||||||
SearchSchema,
|
|
||||||
SortSchema,
|
|
||||||
} from '$lib/validations/zod-schemas';
|
|
||||||
|
|
||||||
// Search a user's collection
|
// Search a user's collection
|
||||||
export const GET = async ({ url, locals }) => {
|
export const GET = async ({ url, locals }) => {
|
||||||
const searchParams = Object.fromEntries(url.searchParams);
|
const searchParams = Object.fromEntries(url.searchParams)
|
||||||
|
|
||||||
const searchGames = PaginationSchema.merge(FilterSchema)
|
const searchGames = PaginationSchema.merge(FilterSchema).merge(SortSchema).merge(SearchSchema).parse(searchParams)
|
||||||
.merge(SortSchema)
|
|
||||||
.merge(SearchSchema)
|
|
||||||
.parse(searchParams);
|
|
||||||
|
|
||||||
if (searchGames.status !== 'success') {
|
if (searchGames.status !== 'success') {
|
||||||
error(400, 'Invalid request');
|
error(400, 'Invalid request')
|
||||||
}
|
}
|
||||||
|
|
||||||
const q = searchParams?.q?.trim() || '';
|
const q = searchParams?.q?.trim() || ''
|
||||||
const limit = parseInt(searchParams?.limit) || 10;
|
const limit = parseInt(searchParams?.limit) || 10
|
||||||
const skip = parseInt(searchParams?.skip) || 0;
|
const skip = parseInt(searchParams?.skip) || 0
|
||||||
const order: OrderDirection = searchParams?.order === 'desc' ? 'desc' : 'asc';
|
const order: OrderDirection = searchParams?.order === 'desc' ? 'desc' : 'asc'
|
||||||
const exact = searchParams?.exact === 'true';
|
const exact = searchParams?.exact === 'true'
|
||||||
let orderBy = searchParams?.orderBy || 'slug';
|
let orderBy = searchParams?.orderBy || 'slug'
|
||||||
|
|
||||||
if (orderBy === 'name') {
|
if (orderBy === 'name') {
|
||||||
orderBy = 'slug';
|
orderBy = 'slug'
|
||||||
}
|
}
|
||||||
console.log(
|
console.log(`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}, orderBy: ${orderBy}`)
|
||||||
`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}, orderBy: ${orderBy}`,
|
console.log(exact)
|
||||||
);
|
|
||||||
console.log(exact);
|
|
||||||
if (exact) {
|
if (exact) {
|
||||||
console.log('Exact Search API');
|
console.log('Exact Search API')
|
||||||
const game = await db.query.games.findFirst({
|
const game = await db.query.games.findFirst({
|
||||||
where: eq(games.name, q),
|
where: eq(games.name, q),
|
||||||
columns: {
|
columns: {
|
||||||
|
|
@ -47,14 +37,14 @@ export const GET = async ({ url, locals }) => {
|
||||||
slug: true,
|
slug: true,
|
||||||
thumb_url: true,
|
thumb_url: true,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
if (!game) {
|
if (!game) {
|
||||||
error(404, { message: 'No games found' });
|
error(404, { message: 'No gamesTable found' })
|
||||||
}
|
}
|
||||||
const foundGames = [game];
|
const foundGames = [game]
|
||||||
console.log('Games found in Exact Search API', JSON.stringify(foundGames, null, 2));
|
console.log('Games found in Exact Search API', JSON.stringify(foundGames, null, 2))
|
||||||
return json(foundGames);
|
return json(foundGames)
|
||||||
} else {
|
} else {
|
||||||
const foundGames =
|
const foundGames =
|
||||||
(await db
|
(await db
|
||||||
|
|
@ -68,37 +58,37 @@ export const GET = async ({ url, locals }) => {
|
||||||
.where(or(ilike(games.name, `%${q}%`), ilike(games.slug, `%${kebabCase(q)}%`)))
|
.where(or(ilike(games.name, `%${q}%`), ilike(games.slug, `%${kebabCase(q)}%`)))
|
||||||
.orderBy(getOrderDirection(order)(getOrderBy(orderBy)))
|
.orderBy(getOrderDirection(order)(getOrderBy(orderBy)))
|
||||||
.offset(skip)
|
.offset(skip)
|
||||||
.limit(limit)) || [];
|
.limit(limit)) || []
|
||||||
// const foundGames = await db.select({
|
// const foundGames = await db.select({
|
||||||
// id: games.id,
|
// id: gamesTable.id,
|
||||||
// name: games.name,
|
// name: gamesTable.name,
|
||||||
// slug: games.slug,
|
// slug: gamesTable.slug,
|
||||||
// thumb_url: games.thumb_url
|
// thumb_url: gamesTable.thumb_url
|
||||||
// })
|
// })
|
||||||
// .from(games)
|
// .from(gamesTable)
|
||||||
// .where(sql`to_tsvector('simple', ${games.name}) || to_tsvector('simple', ${games.slug}) @@ to_tsquery('simple', ${q})`)
|
// .where(sql`to_tsvector('simple', ${gamesTable.name}) || to_tsvector('simple', ${gamesTable.slug}) @@ to_tsquery('simple', ${q})`)
|
||||||
// .orderBy(sql`${orderBy} ${order}`).offset(skip).limit(limit) || [];
|
// .orderBy(sql`${orderBy} ${order}`).offset(skip).limit(limit) || [];
|
||||||
if (foundGames.length === 0) {
|
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));
|
console.log('Games found in Search API', JSON.stringify(foundGames, null, 2))
|
||||||
return json(foundGames);
|
return json(foundGames)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
type OrderDirection = 'asc' | 'desc';
|
type OrderDirection = 'asc' | 'desc'
|
||||||
|
|
||||||
const getOrderDirection = (direction: OrderDirection) => {
|
const getOrderDirection = (direction: OrderDirection) => {
|
||||||
return direction === 'asc' ? asc : desc;
|
return direction === 'asc' ? asc : desc
|
||||||
};
|
}
|
||||||
|
|
||||||
const getOrderBy = (orderBy: string) => {
|
const getOrderBy = (orderBy: string) => {
|
||||||
switch (orderBy) {
|
switch (orderBy) {
|
||||||
case 'name':
|
case 'name':
|
||||||
return games.name;
|
return games.name
|
||||||
case 'slug':
|
case 'slug':
|
||||||
return games.slug;
|
return games.slug
|
||||||
default:
|
default:
|
||||||
return games.slug;
|
return games.slug
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 }) {
|
export async function GET({ locals, params }) {
|
||||||
try {
|
try {
|
||||||
return await getPublisher(locals, params.id);
|
return await getPublisher(locals, params.id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
return new Response('Could not get publishers', {
|
return new Response('Could not get publishersTable', {
|
||||||
status: 500,
|
status: 500,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PUT({ locals, params, request }) {
|
export async function PUT({ locals, params, request }) {
|
||||||
const data: Publishers = await request.json();
|
const data: Publishers = await request.json()
|
||||||
const publisherId = params.id;
|
const publisherId = params.id
|
||||||
return await updatePublisher(locals, data, publisherId);
|
return await updatePublisher(locals, data, publisherId)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
package.json
16
package.json
|
|
@ -27,13 +27,13 @@
|
||||||
"@faker-js/faker": "^8.4.1",
|
"@faker-js/faker": "^8.4.1",
|
||||||
"@melt-ui/pp": "^0.3.2",
|
"@melt-ui/pp": "^0.3.2",
|
||||||
"@melt-ui/svelte": "^0.83.0",
|
"@melt-ui/svelte": "^0.83.0",
|
||||||
"@playwright/test": "^1.46.1",
|
"@playwright/test": "^1.47.0",
|
||||||
"@sveltejs/adapter-auto": "^3.2.4",
|
"@sveltejs/adapter-auto": "^3.2.4",
|
||||||
"@sveltejs/enhanced-img": "^0.3.4",
|
"@sveltejs/enhanced-img": "^0.3.4",
|
||||||
"@sveltejs/kit": "^2.5.25",
|
"@sveltejs/kit": "^2.5.26",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/node": "^20.16.3",
|
"@types/node": "^20.16.5",
|
||||||
"@types/pg": "^8.11.8",
|
"@types/pg": "^8.11.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||||
"@typescript-eslint/parser": "^7.18.0",
|
"@typescript-eslint/parser": "^7.18.0",
|
||||||
|
|
@ -41,19 +41,19 @@
|
||||||
"drizzle-kit": "^0.23.2",
|
"drizzle-kit": "^0.23.2",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.43.0",
|
"eslint-plugin-svelte": "2.36.0-next.13",
|
||||||
"just-clone": "^6.2.0",
|
"just-clone": "^6.2.0",
|
||||||
"just-debounce-it": "^3.2.0",
|
"just-debounce-it": "^3.2.0",
|
||||||
"lucia": "3.2.0",
|
"lucia": "3.2.0",
|
||||||
"lucide-svelte": "^0.408.0",
|
"lucide-svelte": "^0.408.0",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.9.15",
|
||||||
"postcss": "^8.4.44",
|
"postcss": "^8.4.45",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-load-config": "^5.1.0",
|
"postcss-load-config": "^5.1.0",
|
||||||
"postcss-preset-env": "^9.6.0",
|
"postcss-preset-env": "^9.6.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier-plugin-svelte": "^3.2.6",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.78.0",
|
||||||
"satori": "^0.10.14",
|
"satori": "^0.10.14",
|
||||||
"satori-html": "^0.3.2",
|
"satori-html": "^0.3.2",
|
||||||
"svelte": "5.0.0-next.175",
|
"svelte": "5.0.0-next.175",
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
"arctic": "^1.9.2",
|
"arctic": "^1.9.2",
|
||||||
"bits-ui": "^0.21.13",
|
"bits-ui": "^0.21.13",
|
||||||
"boardgamegeekclient": "^1.9.1",
|
"boardgamegeekclient": "^1.9.1",
|
||||||
"bullmq": "^5.12.13",
|
"bullmq": "^5.12.14",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cookie": "^0.6.0",
|
"cookie": "^0.6.0",
|
||||||
|
|
@ -130,4 +130,4 @@
|
||||||
"tsyringe": "^4.8.0",
|
"tsyringe": "^4.8.0",
|
||||||
"zod-to-json-schema": "^3.23.2"
|
"zod-to-json-schema": "^3.23.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2372
pnpm-lock.yaml
2372
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,20 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { applyAction, enhance } from '$app/forms';
|
import { applyAction, enhance } from '$app/forms'
|
||||||
import toast from 'svelte-french-toast';
|
import { invalidateAll } from '$app/navigation'
|
||||||
import { ListChecks, ListTodo, LogOut, User } from 'lucide-svelte';
|
import Logo from '$components/logo.svelte'
|
||||||
import * as DropdownMenu from '$components/ui/dropdown-menu';
|
import * as Avatar from '$components/ui/avatar'
|
||||||
import * as Avatar from '$components/ui/avatar';
|
import * as DropdownMenu from '$components/ui/dropdown-menu'
|
||||||
import { invalidateAll } from '$app/navigation';
|
import type { Users } from '$db/schema'
|
||||||
import Logo from '$components/logo.svelte';
|
import { ListChecks, ListTodo, LogOut, User } from 'lucide-svelte'
|
||||||
import type { Users } from '$db/schema';
|
import toast from 'svelte-french-toast'
|
||||||
|
|
||||||
type HeaderProps = {
|
type HeaderProps = {
|
||||||
user: Users | null;
|
user: Users | null
|
||||||
};
|
}
|
||||||
|
|
||||||
let { user = null }: HeaderProps = $props();
|
let { user = null }: HeaderProps = $props()
|
||||||
|
|
||||||
let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)');
|
let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
|
@ -40,10 +40,10 @@
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
<DropdownMenu.Label>Account</DropdownMenu.Label>
|
<DropdownMenu.Label>Account</DropdownMenu.Label>
|
||||||
<DropdownMenu.Separator />
|
<DropdownMenu.Separator />
|
||||||
<a href="/profile">
|
<a href="/settings">
|
||||||
<DropdownMenu.Item>
|
<DropdownMenu.Item>
|
||||||
<User class="mr-2 h-4 w-4" />
|
<User class="mr-2 h-4 w-4" />
|
||||||
<span>Profile</span>
|
<span>Settings</span>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
</a>
|
</a>
|
||||||
<a href="/collections">
|
<a href="/collections">
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores'
|
import { page } from '$app/stores'
|
||||||
|
import type { Route } from '$lib/types'
|
||||||
type Route = {
|
|
||||||
href: string
|
|
||||||
label: string
|
|
||||||
}
|
|
||||||
|
|
||||||
let { children, routes }: { children: unknown; routes: Route[] } = $props()
|
let { children, routes }: { children: unknown; routes: Route[] } = $props()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="mx-auto grid w-full max-w-6xl gap-2">
|
||||||
|
<h1 class="text-3xl font-semibold">Settings</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="security-nav">
|
<div class="security-nav">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
{#each routes as { href, label }}
|
{#each routes as { href, label }}
|
||||||
<li>
|
<li>
|
||||||
<a href={href} class:active={$page.url.pathname === href}>
|
<a href={href} class:active={$page.url.pathname.includes(href)}>
|
||||||
{label}
|
{label}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -62,8 +62,8 @@ let { children, routes }: { children: unknown; routes: Route[] } = $props()
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: #23527c;
|
color: var(--color-link-hover);
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
background-color: #e9ecef;
|
background-color: #e9ecef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils";
|
import { type Variant, badgeVariants } from "./index.js";
|
||||||
import { badgeVariants, type Variant } from ".";
|
import { cn } from "$lib/utils/ui.js";
|
||||||
|
|
||||||
let className: string | undefined | null = undefined;
|
let className: string | undefined | null = undefined;
|
||||||
export let href: string | undefined = undefined;
|
export let href: string | undefined = undefined;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
import { tv, type VariantProps } from "tailwind-variants";
|
import { type VariantProps, tv } from "tailwind-variants";
|
||||||
export { default as Badge } from "./badge.svelte";
|
export { default as Badge } from "./badge.svelte";
|
||||||
|
|
||||||
export const badgeVariants = tv({
|
export const badgeVariants = tv({
|
||||||
base: "inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none select-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
base: "focus:ring-ring inline-flex select-none items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default: "bg-primary text-primary-foreground hover:bg-primary/80 border-transparent",
|
||||||
"bg-primary hover:bg-primary/80 border-transparent text-primary-foreground",
|
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground",
|
"bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent",
|
||||||
outline: "text-foreground"
|
outline: "text-foreground",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default"
|
variant: "default",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Variant = VariantProps<typeof badgeVariants>["variant"];
|
export type Variant = VariantProps<typeof badgeVariants>["variant"];
|
||||||
|
|
|
||||||
38
src/lib/server/api/common/config.ts
Normal file
38
src/lib/server/api/common/config.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import env from './env'
|
||||||
|
import type { Config } from './types/config'
|
||||||
|
|
||||||
|
const isPreview = process.env.VERCEL_ENV === 'preview' || process.env.VERCEL_ENV === 'development'
|
||||||
|
|
||||||
|
let domain: string
|
||||||
|
if (process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production') {
|
||||||
|
domain = 'boredgame.vercel.app'
|
||||||
|
} else if (isPreview && process.env.VERCEL_BRANCH_URL !== undefined) {
|
||||||
|
domain = process.env.VERCEL_BRANCH_URL
|
||||||
|
} else {
|
||||||
|
domain = 'localhost'
|
||||||
|
}
|
||||||
|
|
||||||
|
// export const config = { ...env, isProduction: process.env.NODE_ENV === 'production'
|
||||||
|
// || process.env.VERCEL_ENV === 'production', domain };
|
||||||
|
|
||||||
|
export const config: Config = {
|
||||||
|
isProduction: process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production',
|
||||||
|
domain,
|
||||||
|
api: {
|
||||||
|
origin: env.ORIGIN,
|
||||||
|
},
|
||||||
|
redis: {
|
||||||
|
url: env.REDIS_URL,
|
||||||
|
},
|
||||||
|
postgres: {
|
||||||
|
user: env.DATABASE_USER,
|
||||||
|
password: env.DATABASE_PASSWORD,
|
||||||
|
host: env.DATABASE_HOST,
|
||||||
|
port: env.DATABASE_PORT,
|
||||||
|
database: env.DATABASE_DB,
|
||||||
|
ssl: env.DATABASE_HOST !== 'localhost',
|
||||||
|
max: env.DB_MIGRATING || env.DB_SEEDING ? 1 : undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('config', config)
|
||||||
|
|
@ -10,8 +10,6 @@ const stringBoolean = z.coerce
|
||||||
.default('false')
|
.default('false')
|
||||||
|
|
||||||
const EnvSchema = z.object({
|
const EnvSchema = z.object({
|
||||||
ADMIN_USERNAME: z.string(),
|
|
||||||
ADMIN_PASSWORD: z.string(),
|
|
||||||
DATABASE_USER: z.string(),
|
DATABASE_USER: z.string(),
|
||||||
DATABASE_PASSWORD: z.string(),
|
DATABASE_PASSWORD: z.string(),
|
||||||
DATABASE_HOST: z.string(),
|
DATABASE_HOST: z.string(),
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import type { DatabaseProvider } from '$lib/server/api/providers/database.provider'
|
|
||||||
|
|
||||||
export interface Repository {
|
|
||||||
trxHost(trx: DatabaseProvider): any
|
|
||||||
}
|
|
||||||
3
src/lib/server/api/common/types/async-service.ts
Normal file
3
src/lib/server/api/common/types/async-service.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export abstract class AsyncService {
|
||||||
|
async init(): Promise<void> {}
|
||||||
|
}
|
||||||
33
src/lib/server/api/common/types/config.ts
Normal file
33
src/lib/server/api/common/types/config.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
export interface Config {
|
||||||
|
isProduction: boolean
|
||||||
|
domain: string
|
||||||
|
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 {
|
||||||
|
user: string
|
||||||
|
password: string
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
database: string
|
||||||
|
ssl: boolean
|
||||||
|
max: number | undefined
|
||||||
|
}
|
||||||
11
src/lib/server/api/common/types/controller.ts
Normal file
11
src/lib/server/api/common/types/controller.ts
Normal 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, '/'>
|
||||||
|
}
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import env from '../../../../env';
|
|
||||||
|
|
||||||
const isPreview = process.env.VERCEL_ENV === 'preview' || process.env.VERCEL_ENV === 'development';
|
|
||||||
|
|
||||||
let domain: string;
|
|
||||||
if (process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production') {
|
|
||||||
domain = 'boredgame.vercel.app';
|
|
||||||
} else if (isPreview && process.env.VERCEL_BRANCH_URL !== undefined) {
|
|
||||||
domain = process.env.VERCEL_BRANCH_URL;
|
|
||||||
} else {
|
|
||||||
domain = 'localhost';
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config = { ...env, isProduction: process.env.NODE_ENV === 'production'
|
|
||||||
|| process.env.VERCEL_ENV === 'production', domain };
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
import 'reflect-metadata'
|
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 { CollectionsService } from '$lib/server/api/services/collections.service'
|
||||||
import { Hono } from 'hono'
|
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { requireAuth } from '../middleware/auth.middleware'
|
import { requireAuth } from '../middleware/auth.middleware'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CollectionController implements Controller {
|
export class CollectionController extends Controller {
|
||||||
controller = new Hono<HonoTypes>()
|
constructor(@inject(CollectionsService) private readonly collectionsService: CollectionsService) {
|
||||||
|
super()
|
||||||
constructor(@inject(CollectionsService) private readonly collectionsService: CollectionsService) {}
|
}
|
||||||
|
|
||||||
routes() {
|
routes() {
|
||||||
return this.controller
|
return this.controller
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,24 @@
|
||||||
import { StatusCodes } from '$lib/constants/status-codes'
|
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 { updateEmailDto } from '$lib/server/api/dtos/update-email.dto'
|
||||||
import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto'
|
import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto'
|
||||||
import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'
|
import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'
|
||||||
import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'
|
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 { IamService } from '$lib/server/api/services/iam.service'
|
||||||
|
import { LuciaService } from '$lib/server/api/services/lucia.service'
|
||||||
import { zValidator } from '@hono/zod-validator'
|
import { zValidator } from '@hono/zod-validator'
|
||||||
import { Hono } from 'hono'
|
|
||||||
import { setCookie } from 'hono/cookie'
|
import { setCookie } from 'hono/cookie'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { requireAuth } from '../middleware/auth.middleware'
|
import { requireAuth } from '../middleware/auth.middleware'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class IamController implements Controller {
|
export class IamController extends Controller {
|
||||||
controller = new Hono<HonoTypes>()
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(IamService) private readonly iamService: IamService,
|
@inject(IamService) private readonly iamService: IamService,
|
||||||
@inject(LuciaProvider) private lucia: LuciaProvider,
|
@inject(LuciaService) private luciaService: LuciaService,
|
||||||
) {}
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
routes() {
|
routes() {
|
||||||
return this.controller
|
return this.controller
|
||||||
|
|
@ -59,7 +57,7 @@ export class IamController implements Controller {
|
||||||
.post('/logout', requireAuth, async (c) => {
|
.post('/logout', requireAuth, async (c) => {
|
||||||
const sessionId = c.var.session.id
|
const sessionId = c.var.session.id
|
||||||
await this.iamService.logout(sessionId)
|
await this.iamService.logout(sessionId)
|
||||||
const sessionCookie = this.lucia.createBlankSessionCookie()
|
const sessionCookie = this.luciaService.lucia.createBlankSessionCookie()
|
||||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||||
path: sessionCookie.attributes.path,
|
path: sessionCookie.attributes.path,
|
||||||
maxAge: sessionCookie.attributes.maxAge,
|
maxAge: sessionCookie.attributes.maxAge,
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,28 @@
|
||||||
import 'reflect-metadata'
|
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 { 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 { zValidator } from '@hono/zod-validator'
|
||||||
import { Hono } from 'hono'
|
|
||||||
import { setCookie } from 'hono/cookie'
|
import { setCookie } from 'hono/cookie'
|
||||||
import { TimeSpan } from 'oslo'
|
import { TimeSpan } from 'oslo'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { limiter } from '../middleware/rate-limiter.middleware'
|
import { limiter } from '../middleware/rate-limiter.middleware'
|
||||||
import { LoginRequestsService } from '../services/loginrequest.service'
|
import { LoginRequestsService } from '../services/loginrequest.service'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LoginController implements Controller {
|
export class LoginController extends Controller {
|
||||||
controller = new Hono<HonoTypes>()
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(LoginRequestsService) private readonly loginRequestsService: LoginRequestsService,
|
@inject(LoginRequestsService) private readonly loginRequestsService: LoginRequestsService,
|
||||||
@inject(LuciaProvider) private lucia: LuciaProvider,
|
@inject(LuciaService) private luciaService: LuciaService,
|
||||||
) {}
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
routes() {
|
routes() {
|
||||||
return this.controller.post('/', zValidator('json', signinUsernameDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
|
return this.controller.post('/', zValidator('json', signinUsernameDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
|
||||||
const { username, password } = c.req.valid('json')
|
const { username, password } = c.req.valid('json')
|
||||||
const session = await this.loginRequestsService.verify({ username, password }, c.req)
|
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)
|
console.log('set cookie', sessionCookie)
|
||||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||||
path: sessionCookie.attributes.path,
|
path: sessionCookie.attributes.path,
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,24 @@
|
||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
import { StatusCodes } from '$lib/constants/status-codes'
|
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 { 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 { RecoveryCodesService } from '$lib/server/api/services/recovery-codes.service'
|
||||||
import { TotpService } from '$lib/server/api/services/totp.service'
|
import { TotpService } from '$lib/server/api/services/totp.service'
|
||||||
import { UsersService } from '$lib/server/api/services/users.service'
|
import { UsersService } from '$lib/server/api/services/users.service'
|
||||||
import { zValidator } from '@hono/zod-validator'
|
import { zValidator } from '@hono/zod-validator'
|
||||||
import { Hono } from 'hono'
|
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { CredentialsType } from '../databases/tables'
|
import { CredentialsType } from '../databases/tables'
|
||||||
import { requireAuth } from '../middleware/auth.middleware'
|
import { requireAuth } from '../middleware/auth.middleware'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class MfaController implements Controller {
|
export class MfaController extends Controller {
|
||||||
controller = new Hono<HonoTypes>()
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(RecoveryCodesService) private readonly recoveryCodesService: RecoveryCodesService,
|
@inject(RecoveryCodesService) private readonly recoveryCodesService: RecoveryCodesService,
|
||||||
@inject(TotpService) private readonly totpService: TotpService,
|
@inject(TotpService) private readonly totpService: TotpService,
|
||||||
@inject(UsersService) private readonly usersService: UsersService,
|
@inject(UsersService) private readonly usersService: UsersService,
|
||||||
) {}
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
routes() {
|
routes() {
|
||||||
return this.controller
|
return this.controller
|
||||||
|
|
@ -52,8 +49,9 @@ export class MfaController implements Controller {
|
||||||
const user = c.var.user
|
const user = c.var.user
|
||||||
// You can only view recovery codes once and that is on creation
|
// You can only view recovery codes once and that is on creation
|
||||||
const existingCodes = await this.recoveryCodesService.findAllRecoveryCodesByUserId(user.id)
|
const existingCodes = await this.recoveryCodesService.findAllRecoveryCodesByUserId(user.id)
|
||||||
if (existingCodes) {
|
if (existingCodes && existingCodes.length > 0) {
|
||||||
return c.body('You have already generated recovery codes', StatusCodes.BAD_REQUEST)
|
console.log('Recovery Codes found', existingCodes)
|
||||||
|
return c.json({ recoveryCodes: existingCodes })
|
||||||
}
|
}
|
||||||
const recoveryCodes = await this.recoveryCodesService.createRecoveryCodes(user.id)
|
const recoveryCodes = await this.recoveryCodesService.createRecoveryCodes(user.id)
|
||||||
return c.json({ recoveryCodes })
|
return c.json({ recoveryCodes })
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,24 @@
|
||||||
import 'reflect-metadata'
|
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 { signupUsernameEmailDto } from '$lib/server/api/dtos/signup-username-email.dto'
|
||||||
import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'
|
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 { 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 { UsersService } from '$lib/server/api/services/users.service'
|
||||||
import { zValidator } from '@hono/zod-validator'
|
import { zValidator } from '@hono/zod-validator'
|
||||||
import { Hono } from 'hono'
|
|
||||||
import { setCookie } from 'hono/cookie'
|
import { setCookie } from 'hono/cookie'
|
||||||
import { TimeSpan } from 'oslo'
|
import { TimeSpan } from 'oslo'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class SignupController implements Controller {
|
export class SignupController extends Controller {
|
||||||
controller = new Hono<HonoTypes>()
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(UsersService) private readonly usersService: UsersService,
|
@inject(UsersService) private readonly usersService: UsersService,
|
||||||
@inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService,
|
@inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService,
|
||||||
@inject(LuciaProvider) private lucia: LuciaProvider,
|
@inject(LuciaService) private luciaService: LuciaService,
|
||||||
) {}
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
routes() {
|
routes() {
|
||||||
return this.controller.post('/', zValidator('json', signupUsernameEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
|
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 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)
|
console.log('set cookie', sessionCookie)
|
||||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||||
path: sessionCookie.attributes.path,
|
path: sessionCookie.attributes.path,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
import 'reflect-metadata'
|
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 { UsersService } from '$lib/server/api/services/users.service'
|
||||||
import { Hono } from 'hono'
|
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { requireAuth } from '../middleware/auth.middleware'
|
import { requireAuth } from '../middleware/auth.middleware'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class UserController implements Controller {
|
export class UserController extends Controller {
|
||||||
controller = new Hono<HonoTypes>()
|
constructor(@inject(UsersService) private readonly usersService: UsersService) {
|
||||||
|
super()
|
||||||
constructor(@inject(UsersService) private readonly usersService: UsersService) {}
|
}
|
||||||
|
|
||||||
routes() {
|
routes() {
|
||||||
return this.controller
|
return this.controller
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
import 'reflect-metadata'
|
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 { WishlistsService } from '$lib/server/api/services/wishlists.service'
|
||||||
import { Hono } from 'hono'
|
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { requireAuth } from '../middleware/auth.middleware'
|
import { requireAuth } from '../middleware/auth.middleware'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class WishlistController implements Controller {
|
export class WishlistController extends Controller {
|
||||||
controller = new Hono<HonoTypes>()
|
constructor(@inject(WishlistsService) private readonly wishlistsService: WishlistsService) {
|
||||||
|
super()
|
||||||
constructor(@inject(WishlistsService) private readonly wishlistsService: WishlistsService) {}
|
}
|
||||||
|
|
||||||
routes() {
|
routes() {
|
||||||
return this.controller
|
return this.controller
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { drizzle } from 'drizzle-orm/postgres-js'
|
||||||
import { migrate } from 'drizzle-orm/postgres-js/migrator'
|
import { migrate } from 'drizzle-orm/postgres-js/migrator'
|
||||||
import postgres from 'postgres'
|
import postgres from 'postgres'
|
||||||
import config from '../../../../../drizzle.config'
|
import config from '../../../../../drizzle.config'
|
||||||
import env from '../../../../env'
|
import env from '../common/env'
|
||||||
|
|
||||||
const connection = postgres({
|
const connection = postgres({
|
||||||
host: env.DATABASE_HOST || 'localhost',
|
host: env.DATABASE_HOST || 'localhost',
|
||||||
|
|
@ -17,7 +17,11 @@ const connection = postgres({
|
||||||
const db = drizzle(connection)
|
const db = drizzle(connection)
|
||||||
|
|
||||||
try {
|
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')
|
console.log('Migrations complete')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,15 @@ CREATE TABLE IF NOT EXISTS "collections" (
|
||||||
CONSTRAINT "collections_cuid_unique" UNIQUE("cuid")
|
CONSTRAINT "collections_cuid_unique" UNIQUE("cuid")
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> 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" (
|
CREATE TABLE IF NOT EXISTS "expansions" (
|
||||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
"cuid" text,
|
"cuid" text,
|
||||||
|
|
@ -65,6 +74,16 @@ CREATE TABLE IF NOT EXISTS "external_ids" (
|
||||||
CONSTRAINT "external_ids_cuid_unique" UNIQUE("cuid")
|
CONSTRAINT "external_ids_cuid_unique" UNIQUE("cuid")
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> 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" (
|
CREATE TABLE IF NOT EXISTS "games" (
|
||||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
"cuid" text,
|
"cuid" text,
|
||||||
|
|
@ -177,9 +196,9 @@ CREATE TABLE IF NOT EXISTS "sessions" (
|
||||||
CREATE TABLE IF NOT EXISTS "two_factor" (
|
CREATE TABLE IF NOT EXISTS "two_factor" (
|
||||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
"cuid" text,
|
"cuid" text,
|
||||||
"two_factor_secret" text NOT NULL,
|
"secret" text NOT NULL,
|
||||||
"two_factor_enabled" boolean DEFAULT false NOT NULL,
|
"enabled" boolean DEFAULT false NOT NULL,
|
||||||
"initiated_time" timestamp with time zone NOT NULL,
|
"initiated_time" timestamp with time zone,
|
||||||
"user_id" uuid NOT NULL,
|
"user_id" uuid NOT NULL,
|
||||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
"updated_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,
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
"cuid" text,
|
"cuid" text,
|
||||||
"username" text,
|
"username" text,
|
||||||
"hashed_password" text,
|
|
||||||
"email" text,
|
"email" text,
|
||||||
"first_name" text,
|
"first_name" text,
|
||||||
"last_name" text,
|
"last_name" text,
|
||||||
"verified" boolean DEFAULT false,
|
"verified" boolean DEFAULT false,
|
||||||
"receive_email" boolean DEFAULT false,
|
"receive_email" boolean DEFAULT false,
|
||||||
|
"mfa_enabled" boolean DEFAULT false NOT NULL,
|
||||||
"theme" text DEFAULT 'system',
|
"theme" text DEFAULT 'system',
|
||||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
"updated_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;
|
WHEN duplicate_object THEN null;
|
||||||
END $$;
|
END $$;
|
||||||
--> statement-breakpoint
|
--> 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
|
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;
|
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
|
EXCEPTION
|
||||||
|
|
@ -290,6 +315,12 @@ EXCEPTION
|
||||||
WHEN duplicate_object THEN null;
|
WHEN duplicate_object THEN null;
|
||||||
END $$;
|
END $$;
|
||||||
--> statement-breakpoint
|
--> 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
|
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;
|
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
|
EXCEPTION
|
||||||
|
|
@ -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";
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE "two_factor" ALTER COLUMN "initiated_time" DROP NOT NULL;
|
|
||||||
|
|
@ -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";
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE "users" ADD COLUMN "enabled" boolean DEFAULT false NOT NULL;
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE "users" RENAME COLUMN "enabled" TO "mfa_enabled";
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"id": "e120d11a-bf28-4c96-9f2f-96e23e23c7e2",
|
"id": "4760134e-48bb-47db-b431-56903dad6e24",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"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": {
|
"public.expansions": {
|
||||||
"name": "expansions",
|
"name": "expansions",
|
||||||
"schema": "",
|
"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": {
|
"public.games": {
|
||||||
"name": "games",
|
"name": "games",
|
||||||
"schema": "",
|
"schema": "",
|
||||||
|
|
@ -1273,14 +1406,14 @@
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
"two_factor_secret": {
|
"secret": {
|
||||||
"name": "two_factor_secret",
|
"name": "secret",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
"two_factor_enabled": {
|
"enabled": {
|
||||||
"name": "two_factor_enabled",
|
"name": "enabled",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
|
|
@ -1290,7 +1423,7 @@
|
||||||
"name": "initiated_time",
|
"name": "initiated_time",
|
||||||
"type": "timestamp with time zone",
|
"type": "timestamp with time zone",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true
|
"notNull": false
|
||||||
},
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
|
|
@ -1461,12 +1594,6 @@
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
"hashed_password": {
|
|
||||||
"name": "hashed_password",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"email": {
|
"email": {
|
||||||
"name": "email",
|
"name": "email",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|
@ -1499,6 +1626,13 @@
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"mfa_enabled": {
|
||||||
|
"name": "mfa_enabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"name": "theme",
|
"name": "theme",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|
@ -1720,6 +1854,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schemas": {},
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"columns": {},
|
"columns": {},
|
||||||
"schemas": {},
|
"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
|
|
@ -5,43 +5,8 @@
|
||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1720625651245,
|
"when": 1725489682980,
|
||||||
"tag": "0000_dazzling_stick",
|
"tag": "0000_volatile_warhawk",
|
||||||
"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",
|
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
import { Table, getTableName, sql } from 'drizzle-orm'
|
import 'reflect-metadata'
|
||||||
import env from '../../../../env'
|
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
|
||||||
import { db, pool } from '../packages/drizzle'
|
import { type Table, getTableName, sql } from 'drizzle-orm'
|
||||||
|
import type { NodePgDatabase } from 'drizzle-orm/node-postgres'
|
||||||
|
import env from '../common/env'
|
||||||
import * as seeds from './seeds'
|
import * as seeds from './seeds'
|
||||||
import * as schema from './tables'
|
import * as schema from './tables'
|
||||||
|
|
||||||
|
const drizzleService = new DrizzleService()
|
||||||
|
|
||||||
if (!env.DB_SEEDING) {
|
if (!env.DB_SEEDING) {
|
||||||
throw new Error('You must set DB_SEEDING to "true" when running seeds')
|
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`))
|
return db.execute(sql.raw(`TRUNCATE TABLE ${getTableName(table)} RESTART IDENTITY CASCADE`))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,33 +23,33 @@ for (const table of [
|
||||||
schema.collection_items,
|
schema.collection_items,
|
||||||
schema.collections,
|
schema.collections,
|
||||||
schema.credentialsTable,
|
schema.credentialsTable,
|
||||||
schema.expansions,
|
schema.expansionsTable,
|
||||||
schema.externalIds,
|
schema.externalIdsTable,
|
||||||
schema.federatedIdentityTable,
|
schema.federatedIdentityTable,
|
||||||
schema.games,
|
schema.gamesTable,
|
||||||
schema.gamesToExternalIds,
|
schema.gamesToExternalIdsTable,
|
||||||
schema.mechanics,
|
schema.mechanicsTable,
|
||||||
schema.mechanicsToExternalIds,
|
schema.mechanicsToExternalIdsTable,
|
||||||
schema.mechanics_to_games,
|
schema.mechanics_to_games,
|
||||||
schema.password_reset_tokens,
|
schema.password_reset_tokens,
|
||||||
schema.publishers,
|
schema.publishersTable,
|
||||||
schema.publishersToExternalIds,
|
schema.publishersToExternalIdsTable,
|
||||||
schema.publishers_to_games,
|
schema.publishers_to_games,
|
||||||
schema.recoveryCodesTable,
|
schema.recoveryCodesTable,
|
||||||
schema.roles,
|
schema.rolesTable,
|
||||||
schema.sessionsTable,
|
schema.sessionsTable,
|
||||||
schema.twoFactorTable,
|
schema.twoFactorTable,
|
||||||
schema.user_roles,
|
schema.user_roles,
|
||||||
schema.usersTable,
|
schema.usersTable,
|
||||||
schema.wishlist_items,
|
schema.wishlist_items,
|
||||||
schema.wishlists,
|
schema.wishlistsTable,
|
||||||
]) {
|
]) {
|
||||||
// await db.delete(table); // clear tables without truncating / resetting ids
|
// 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.roles(drizzleService.db)
|
||||||
await seeds.users(db)
|
await seeds.users(drizzleService.db)
|
||||||
|
|
||||||
await pool.end()
|
await drizzleService.dispose()
|
||||||
process.exit()
|
process.exit()
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import { type db } from '$lib/server/api/packages/drizzle'
|
||||||
import roles from './data/roles.json'
|
import roles from './data/roles.json'
|
||||||
|
|
||||||
export default async function seed(db: db) {
|
export default async function seed(db: db) {
|
||||||
console.log('Creating roles ...')
|
console.log('Creating rolesTable ...')
|
||||||
for (const role of roles) {
|
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.')
|
console.log('Roles created.')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,24 @@
|
||||||
import * as schema from '$lib/server/api/databases/tables'
|
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 { eq } from 'drizzle-orm'
|
||||||
import { Argon2id } from 'oslo/password'
|
import { Argon2id } from 'oslo/password'
|
||||||
import { config } from '../../configs/config'
|
import { config } from '../../common/config'
|
||||||
import users from './data/users.json'
|
import users from './data/users.json'
|
||||||
|
|
||||||
type JsonUser = {
|
|
||||||
id: string
|
|
||||||
username: string
|
|
||||||
email: string
|
|
||||||
password: string
|
|
||||||
roles: {
|
|
||||||
name: string
|
|
||||||
primary: boolean
|
|
||||||
}[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonRole = {
|
type JsonRole = {
|
||||||
name: string
|
name: string
|
||||||
primary: boolean
|
primary: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function seed(db: db) {
|
export default async function seed(db: db) {
|
||||||
const adminRole = await db.select().from(schema.roles).where(eq(schema.roles.name, 'admin'))
|
const adminRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'admin'))
|
||||||
const userRole = await db.select().from(schema.roles).where(eq(schema.roles.name, 'user'))
|
const userRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'user'))
|
||||||
|
|
||||||
console.log('Admin Role: ', adminRole)
|
console.log('Admin Role: ', adminRole)
|
||||||
const adminUser = await db
|
const adminUser = await db
|
||||||
.insert(schema.usersTable)
|
.insert(schema.usersTable)
|
||||||
.values({
|
.values({
|
||||||
username: `${config.ADMIN_USERNAME}`,
|
username: `${process.env.ADMIN_USERNAME}`,
|
||||||
email: '',
|
email: '',
|
||||||
first_name: 'Brad',
|
first_name: 'Brad',
|
||||||
last_name: 'S',
|
last_name: 'S',
|
||||||
|
|
@ -43,12 +32,12 @@ export default async function seed(db: db) {
|
||||||
await db.insert(schema.credentialsTable).values({
|
await db.insert(schema.credentialsTable).values({
|
||||||
user_id: adminUser[0].id,
|
user_id: adminUser[0].id,
|
||||||
type: schema.CredentialsType.PASSWORD,
|
type: schema.CredentialsType.PASSWORD,
|
||||||
secret_data: await new Argon2id().hash(`${config.ADMIN_PASSWORD}`),
|
secret_data: await new Argon2id().hash(`${process.env.ADMIN_PASSWORD}`),
|
||||||
})
|
})
|
||||||
|
|
||||||
await db.insert(schema.collections).values({ user_id: adminUser[0].id }).onConflictDoNothing()
|
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
|
await db
|
||||||
.insert(schema.user_roles)
|
.insert(schema.user_roles)
|
||||||
|
|
@ -85,11 +74,11 @@ export default async function seed(db: db) {
|
||||||
secret_data: await new Argon2id().hash(user.password),
|
secret_data: await new Argon2id().hash(user.password),
|
||||||
})
|
})
|
||||||
await db.insert(schema.collections).values({ user_id: insertedUser?.id })
|
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(
|
await Promise.all(
|
||||||
user.roles.map(async (role: JsonRole) => {
|
user.roles.map(async (role: JsonRole) => {
|
||||||
const foundRole = await db.query.roles.findFirst({
|
const foundRole = await db.query.rolesTable.findFirst({
|
||||||
where: eq(schema.roles.name, role.name),
|
where: eq(schema.rolesTable.name, role.name),
|
||||||
})
|
})
|
||||||
if (!foundRole) {
|
if (!foundRole) {
|
||||||
throw new Error('Role not found')
|
throw new Error('Role not found')
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
import { categoriesToExternalIdsTable } from './categoriesToExternalIdsTable'
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { categories_to_games_table } from './categoriesToGames'
|
import { categoriesToExternalIdsTable } from './categoriesToExternalIds.table'
|
||||||
|
import { categories_to_games_table } from './categoriesToGames.table'
|
||||||
|
|
||||||
export const categoriesTable = pgTable('categories', {
|
export const categoriesTable = pgTable('categories', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
@ -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],
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
import { relations } from 'drizzle-orm'
|
||||||
import { relations } from 'drizzle-orm';
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
|
||||||
import { categoriesTable } from './categories.table';
|
import { gamesTable } from '././games.table'
|
||||||
import { games } from './games';
|
import { categoriesTable } from './categories.table'
|
||||||
|
|
||||||
export const categories_to_games_table = pgTable(
|
export const categories_to_games_table = pgTable(
|
||||||
'categories_to_games',
|
'categories_to_games',
|
||||||
|
|
@ -11,25 +11,24 @@ export const categories_to_games_table = pgTable(
|
||||||
.references(() => categoriesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
.references(() => categoriesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
game_id: uuid('game_id')
|
game_id: uuid('game_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
.references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
},
|
},
|
||||||
(table) => {
|
(table) => {
|
||||||
return {
|
return {
|
||||||
categoriesToGamesPkey: primaryKey({
|
categoriesToGamesPkey: primaryKey({
|
||||||
columns: [table.category_id, table.game_id],
|
columns: [table.category_id, table.game_id],
|
||||||
}),
|
}),
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
export const categories_to_games_relations = relations(categories_to_games_table, ({ one }) => ({
|
export const categories_to_games_relations = relations(categories_to_games_table, ({ one }) => ({
|
||||||
category: one(categoriesTable, {
|
category: one(categoriesTable, {
|
||||||
fields: [categories_to_games_table.category_id],
|
fields: [categories_to_games_table.category_id],
|
||||||
references: [categoriesTable.id],
|
references: [categoriesTable.id],
|
||||||
}),
|
}),
|
||||||
game: one(games, {
|
game: one(gamesTable, {
|
||||||
fields: [categories_to_games_table.game_id],
|
fields: [categories_to_games_table.game_id],
|
||||||
references: [games.id],
|
references: [gamesTable.id],
|
||||||
}),
|
}),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { integer, pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { integer, pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
import { collections } from './collections'
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { games } from './games'
|
import { gamesTable } from '././games.table'
|
||||||
|
import { collections } from './collections.table'
|
||||||
|
|
||||||
export const collection_items = pgTable('collection_items', {
|
export const collection_items = pgTable('collection_items', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
|
@ -15,20 +15,20 @@ export const collection_items = pgTable('collection_items', {
|
||||||
.references(() => collections.id, { onDelete: 'cascade' }),
|
.references(() => collections.id, { onDelete: 'cascade' }),
|
||||||
game_id: uuid('game_id')
|
game_id: uuid('game_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => games.id, { onDelete: 'cascade' }),
|
.references(() => gamesTable.id, { onDelete: 'cascade' }),
|
||||||
times_played: integer('times_played').default(0),
|
times_played: integer('times_played').default(0),
|
||||||
...timestamps,
|
...timestamps,
|
||||||
})
|
})
|
||||||
|
|
||||||
export type CollectionItems = InferSelectModel<typeof collection_items>
|
export type CollectionItemsTable = InferSelectModel<typeof collection_items>
|
||||||
|
|
||||||
export const collection_item_relations = relations(collection_items, ({ one }) => ({
|
export const collection_item_relations = relations(collection_items, ({ one }) => ({
|
||||||
collection: one(collections, {
|
collection: one(collections, {
|
||||||
fields: [collection_items.collection_id],
|
fields: [collection_items.collection_id],
|
||||||
references: [collections.id],
|
references: [collections.id],
|
||||||
}),
|
}),
|
||||||
game: one(games, {
|
game: one(gamesTable, {
|
||||||
fields: [collection_items.game_id],
|
fields: [collection_items.game_id],
|
||||||
references: [games.id],
|
references: [gamesTable.id],
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { usersTable } from './users.table'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export const collections = pgTable('collections', {
|
export const collections = pgTable('collections', {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
import type { InferSelectModel } from 'drizzle-orm'
|
||||||
import { type InferSelectModel } from 'drizzle-orm'
|
|
||||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { usersTable } from './users.table'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export enum CredentialsType {
|
export enum CredentialsType {
|
||||||
|
|
|
||||||
32
src/lib/server/api/databases/tables/expansions.table.ts
Normal file
32
src/lib/server/api/databases/tables/expansions.table.ts
Normal 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],
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
@ -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],
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
16
src/lib/server/api/databases/tables/externalIds.table.ts
Normal file
16
src/lib/server/api/databases/tables/externalIds.table.ts
Normal 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>
|
||||||
|
|
@ -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>;
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { type InferSelectModel } from 'drizzle-orm'
|
import { type InferSelectModel } from 'drizzle-orm'
|
||||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { usersTable } from './users.table'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export const federatedIdentityTable = pgTable('federated_identity', {
|
export const federatedIdentityTable = pgTable('federated_identity', {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations, sql } from 'drizzle-orm'
|
import { type InferSelectModel, relations, sql } from 'drizzle-orm'
|
||||||
import { index, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
import { index, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
||||||
import { categories_to_games_table } from './categoriesToGames'
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { gamesToExternalIds } from './gamesToExternalIds'
|
import { categories_to_games_table } from './categoriesToGames.table'
|
||||||
import { mechanics_to_games } from './mechanicsToGames'
|
import { gamesToExternalIdsTable } from './gamesToExternalIds.table'
|
||||||
import { publishers_to_games } from './publishersToGames'
|
import { mechanics_to_games } from './mechanicsToGames.table'
|
||||||
|
import { publishers_to_games } from './publishersToGames.table'
|
||||||
|
|
||||||
export const games = pgTable(
|
export const gamesTable = pgTable(
|
||||||
'games',
|
'games',
|
||||||
{
|
{
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
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),
|
categories_to_games: many(categories_to_games_table),
|
||||||
mechanics_to_games: many(mechanics_to_games),
|
mechanics_to_games: many(mechanics_to_games),
|
||||||
publishers_to_games: many(publishers_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>
|
||||||
|
|
@ -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],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
@ -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],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
export * from './categories.table';
|
export * from './categories.table'
|
||||||
export * from './categoriesToExternalIdsTable';
|
export * from './categoriesToExternalIds.table'
|
||||||
export * from './categoriesToGames';
|
export * from './categoriesToGames.table'
|
||||||
export * from './collectionItems';
|
export * from './collectionItems.table'
|
||||||
export * from './collections';
|
export * from './collections.table'
|
||||||
export * from './credentials.table';
|
export * from './credentials.table'
|
||||||
export * from './expansions';
|
export * from './expansions.table'
|
||||||
export * from './externalIds';
|
export * from './externalIds.table'
|
||||||
export * from './federatedIdentity.table';
|
export * from './federatedIdentity.table'
|
||||||
export * from './games';
|
export * from './games.table'
|
||||||
export * from './gamesToExternalIds';
|
export * from './gamesToExternalIds.table'
|
||||||
export * from './mechanics';
|
export * from './mechanics.table'
|
||||||
export * from './mechanicsToExternalIds';
|
export * from './mechanicsToExternalIds.table'
|
||||||
export * from './mechanicsToGames'
|
export * from './mechanicsToGames.table'
|
||||||
export * from './passwordResetTokens';
|
export * from './passwordResetTokens.table'
|
||||||
export * from './publishers';
|
export * from './publishers.table'
|
||||||
export * from './publishersToExternalIds';
|
export * from './publishersToExternalIds.table'
|
||||||
export * from './publishersToGames';
|
export * from './publishersToGames.table'
|
||||||
export * from './recovery-codes.table';
|
export * from './recovery-codes.table'
|
||||||
export * from './roles';
|
export * from './roles.table'
|
||||||
export * from './sessions.table';
|
export * from './sessions.table'
|
||||||
export * from './two-factor.table';
|
export * from './two-factor.table'
|
||||||
export * from './userRoles';
|
export * from './userRoles.table'
|
||||||
export * from './users.table';
|
export * from './users.table'
|
||||||
export * from './wishlistItems';
|
export * from './wishlistItems.table'
|
||||||
export * from './wishlists';
|
export * from './wishlists.table'
|
||||||
|
|
|
||||||
23
src/lib/server/api/databases/tables/mechanics.table.ts
Normal file
23
src/lib/server/api/databases/tables/mechanics.table.ts
Normal 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),
|
||||||
|
}))
|
||||||
|
|
@ -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),
|
|
||||||
}))
|
|
||||||
|
|
@ -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],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
@ -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],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
@ -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],
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
@ -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],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
||||||
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { usersTable } from './users.table'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export const password_reset_tokens = pgTable('password_reset_tokens', {
|
export const password_reset_tokens = pgTable('password_reset_tokens', {
|
||||||
|
|
@ -15,7 +15,7 @@ export const password_reset_tokens = pgTable('password_reset_tokens', {
|
||||||
...timestamps,
|
...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 }) => ({
|
export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({
|
||||||
user: one(usersTable, {
|
user: one(usersTable, {
|
||||||
23
src/lib/server/api/databases/tables/publishers.table.ts
Normal file
23
src/lib/server/api/databases/tables/publishers.table.ts
Normal 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),
|
||||||
|
}))
|
||||||
|
|
@ -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),
|
|
||||||
}))
|
|
||||||
|
|
@ -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],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
@ -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],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
import { relations } from 'drizzle-orm'
|
||||||
import { relations } from 'drizzle-orm';
|
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
|
||||||
import {publishers} from './publishers';
|
import { gamesTable } from '././games.table'
|
||||||
import {games} from './games';
|
import { publishersTable } from './publishers.table'
|
||||||
|
|
||||||
export const publishers_to_games = pgTable(
|
export const publishers_to_games = pgTable(
|
||||||
'publishers_to_games',
|
'publishers_to_games',
|
||||||
{
|
{
|
||||||
publisher_id: uuid('publisher_id')
|
publisher_id: uuid('publisher_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
.references(() => publishersTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
game_id: uuid('game_id')
|
game_id: uuid('game_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
.references(() => gamesTable.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||||
},
|
},
|
||||||
(table) => {
|
(table) => {
|
||||||
return {
|
return {
|
||||||
publishersToGamesPkey: primaryKey({
|
publishersToGamesPkey: primaryKey({
|
||||||
columns: [table.publisher_id, table.game_id],
|
columns: [table.publisher_id, table.game_id],
|
||||||
}),
|
}),
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({
|
export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({
|
||||||
publisher: one(publishers, {
|
publisher: one(publishersTable, {
|
||||||
fields: [publishers_to_games.publisher_id],
|
fields: [publishers_to_games.publisher_id],
|
||||||
references: [publishers.id],
|
references: [publishersTable.id],
|
||||||
}),
|
}),
|
||||||
game: one(games, {
|
game: one(gamesTable, {
|
||||||
fields: [publishers_to_games.game_id],
|
fields: [publishers_to_games.game_id],
|
||||||
references: [games.id],
|
references: [gamesTable.id],
|
||||||
}),
|
}),
|
||||||
}));
|
}))
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import type { InferSelectModel } from 'drizzle-orm'
|
import type { InferSelectModel } from 'drizzle-orm'
|
||||||
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { usersTable } from './users.table'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export const recoveryCodesTable = pgTable('recovery_codes', {
|
export const recoveryCodesTable = pgTable('recovery_codes', {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
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(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
cuid: text('cuid')
|
cuid: text('cuid')
|
||||||
.unique()
|
.unique()
|
||||||
|
|
@ -14,8 +14,8 @@ export const roles = pgTable('roles', {
|
||||||
...timestamps,
|
...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),
|
user_roles: many(user_roles),
|
||||||
}))
|
}))
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
||||||
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { usersTable } from './users.table'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export const twoFactorTable = pgTable('two_factor', {
|
export const twoFactorTable = pgTable('two_factor', {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
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'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export const user_roles = pgTable('user_roles', {
|
export const user_roles = pgTable('user_roles', {
|
||||||
|
|
@ -15,15 +15,15 @@ export const user_roles = pgTable('user_roles', {
|
||||||
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
||||||
role_id: uuid('role_id')
|
role_id: uuid('role_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.id, { onDelete: 'cascade' }),
|
.references(() => rolesTable.id, { onDelete: 'cascade' }),
|
||||||
primary: boolean('primary').default(false),
|
primary: boolean('primary').default(false),
|
||||||
...timestamps,
|
...timestamps,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const user_role_relations = relations(user_roles, ({ one }) => ({
|
export const user_role_relations = relations(user_roles, ({ one }) => ({
|
||||||
role: one(roles, {
|
role: one(rolesTable, {
|
||||||
fields: [user_roles.role_id],
|
fields: [user_roles.role_id],
|
||||||
references: [roles.id],
|
references: [rolesTable.id],
|
||||||
}),
|
}),
|
||||||
user: one(usersTable, {
|
user: one(usersTable, {
|
||||||
fields: [user_roles.user_id],
|
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>
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
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', {
|
export const usersTable = pgTable('users', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
import { games } from './games'
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { wishlists } from './wishlists'
|
import { gamesTable } from '././games.table'
|
||||||
|
import { wishlistsTable } from './wishlists.table'
|
||||||
|
|
||||||
export const wishlist_items = pgTable('wishlist_items', {
|
export const wishlist_items = pgTable('wishlist_items', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
|
@ -12,22 +12,22 @@ export const wishlist_items = pgTable('wishlist_items', {
|
||||||
.$defaultFn(() => cuid2()),
|
.$defaultFn(() => cuid2()),
|
||||||
wishlist_id: uuid('wishlist_id')
|
wishlist_id: uuid('wishlist_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => wishlists.id, { onDelete: 'cascade' }),
|
.references(() => wishlistsTable.id, { onDelete: 'cascade' }),
|
||||||
game_id: uuid('game_id')
|
game_id: uuid('game_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => games.id, { onDelete: 'cascade' }),
|
.references(() => gamesTable.id, { onDelete: 'cascade' }),
|
||||||
...timestamps,
|
...timestamps,
|
||||||
})
|
})
|
||||||
|
|
||||||
export type WishlistItems = InferSelectModel<typeof wishlist_items>
|
export type WishlistItemsTable = InferSelectModel<typeof wishlist_items>
|
||||||
|
|
||||||
export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
|
export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
|
||||||
wishlist: one(wishlists, {
|
wishlist: one(wishlistsTable, {
|
||||||
fields: [wishlist_items.wishlist_id],
|
fields: [wishlist_items.wishlist_id],
|
||||||
references: [wishlists.id],
|
references: [wishlistsTable.id],
|
||||||
}),
|
}),
|
||||||
game: one(games, {
|
game: one(gamesTable, {
|
||||||
fields: [wishlist_items.game_id],
|
fields: [wishlist_items.game_id],
|
||||||
references: [games.id],
|
references: [gamesTable.id],
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { timestamps } from '$lib/server/api/common/utils/table.utils'
|
|
||||||
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
import { createId as cuid2 } from '@paralleldrive/cuid2'
|
||||||
import { type InferSelectModel, relations } from 'drizzle-orm'
|
import { type InferSelectModel, relations } from 'drizzle-orm'
|
||||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
|
||||||
|
import { timestamps } from '../../common/utils/table'
|
||||||
import { usersTable } from './users.table'
|
import { usersTable } from './users.table'
|
||||||
|
|
||||||
export const wishlists = pgTable('wishlists', {
|
export const wishlistsTable = pgTable('wishlists', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
cuid: text('cuid')
|
cuid: text('cuid')
|
||||||
.unique()
|
.unique()
|
||||||
|
|
@ -16,11 +16,11 @@ export const wishlists = pgTable('wishlists', {
|
||||||
...timestamps,
|
...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, {
|
user: one(usersTable, {
|
||||||
fields: [wishlists.user_id],
|
fields: [wishlistsTable.user_id],
|
||||||
references: [usersTable.id],
|
references: [usersTable.id],
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
@ -10,7 +10,7 @@ import { hc } from 'hono/client'
|
||||||
import { cors } from 'hono/cors'
|
import { cors } from 'hono/cors'
|
||||||
import { logger } from 'hono/logger'
|
import { logger } from 'hono/logger'
|
||||||
import { container } from 'tsyringe'
|
import { container } from 'tsyringe'
|
||||||
import { config } from './configs/config'
|
import { config } from './common/config'
|
||||||
import { IamController } from './controllers/iam.controller'
|
import { IamController } from './controllers/iam.controller'
|
||||||
import { LoginController } from './controllers/login.controller'
|
import { LoginController } from './controllers/login.controller'
|
||||||
import { validateAuthSession, verifyOrigin } from './middleware/auth.middleware'
|
import { validateAuthSession, verifyOrigin } from './middleware/auth.middleware'
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
|
import { LuciaService } from '$lib/server/api/services/lucia.service'
|
||||||
import type { MiddlewareHandler } from 'hono'
|
import type { MiddlewareHandler } from 'hono'
|
||||||
import { createMiddleware } from 'hono/factory'
|
import { createMiddleware } from 'hono/factory'
|
||||||
import type { Session, User } from 'lucia'
|
import type { Session, User } from 'lucia'
|
||||||
import { verifyRequestOrigin } from 'oslo/request'
|
import { verifyRequestOrigin } from 'oslo/request'
|
||||||
|
import { container } from 'tsyringe'
|
||||||
import { Unauthorized } from '../common/exceptions'
|
import { Unauthorized } from '../common/exceptions'
|
||||||
import { lucia } from '../packages/lucia'
|
import type { HonoTypes } from '../common/types/hono'
|
||||||
import type { HonoTypes } from '../types'
|
|
||||||
|
// resolve dependencies from the container
|
||||||
|
const { lucia } = container.resolve(LuciaService)
|
||||||
|
|
||||||
export const verifyOrigin: MiddlewareHandler<HonoTypes> = createMiddleware(async (c, next) => {
|
export const verifyOrigin: MiddlewareHandler<HonoTypes> = createMiddleware(async (c, next) => {
|
||||||
if (c.req.method === 'GET') {
|
if (c.req.method === 'GET') {
|
||||||
|
|
@ -27,7 +31,7 @@ export const validateAuthSession: MiddlewareHandler<HonoTypes> = createMiddlewar
|
||||||
}
|
}
|
||||||
|
|
||||||
const { session, user } = await lucia.validateSession(sessionId)
|
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 })
|
c.header('Set-Cookie', lucia.createSessionCookie(session.id).serialize(), { append: true })
|
||||||
}
|
}
|
||||||
if (!session) {
|
if (!session) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { rateLimiter } from 'hono-rate-limiter'
|
import { rateLimiter } from 'hono-rate-limiter'
|
||||||
import RedisClient from 'ioredis'
|
|
||||||
import { RedisStore } from 'rate-limit-redis'
|
import { RedisStore } from 'rate-limit-redis'
|
||||||
import { config } from '../configs/config'
|
import { container } from 'tsyringe'
|
||||||
import type { HonoTypes } from '../types'
|
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({
|
export function limiter({
|
||||||
limit,
|
limit,
|
||||||
|
|
|
||||||
15
src/lib/server/api/middleware/require-auth.middleware.ts
Normal file
15
src/lib/server/api/middleware/require-auth.middleware.ts
Normal 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()
|
||||||
|
})
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { drizzle } from 'drizzle-orm/node-postgres'
|
import { drizzle } from 'drizzle-orm/node-postgres'
|
||||||
import pg from 'pg'
|
import pg from 'pg'
|
||||||
import { config } from '../configs/config'
|
import { config } from '../common/config'
|
||||||
import * as schema from '../databases/tables'
|
import * as schema from '../databases/tables'
|
||||||
|
|
||||||
// create the connection
|
// create the connection
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import RedisClient from 'ioredis'
|
import RedisClient from 'ioredis'
|
||||||
import { container } from 'tsyringe'
|
import { container } from 'tsyringe'
|
||||||
import { config } from '../configs/config'
|
import { config } from '../common/config'
|
||||||
|
|
||||||
export const RedisProvider = Symbol('REDIS_TOKEN')
|
export const RedisProvider = Symbol('REDIS_TOKEN')
|
||||||
export type RedisProvider = RedisClient
|
export type RedisProvider = RedisClient
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
|
import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository'
|
||||||
import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository.utils'
|
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
|
||||||
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
|
|
||||||
import { type InferInsertModel, eq } from 'drizzle-orm'
|
import { type InferInsertModel, eq } from 'drizzle-orm'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { collections } from '../databases/tables'
|
import { collections } from '../databases/tables'
|
||||||
|
|
@ -9,15 +8,15 @@ export type CreateCollection = InferInsertModel<typeof collections>
|
||||||
export type UpdateCollection = Partial<CreateCollection>
|
export type UpdateCollection = Partial<CreateCollection>
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CollectionsRepository implements Repository {
|
export class CollectionsRepository {
|
||||||
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
|
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
|
||||||
|
|
||||||
async findAll() {
|
async findAll(db = this.drizzle.db) {
|
||||||
return this.db.query.collections.findMany()
|
return db.query.collections.findMany()
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneById(id: string) {
|
async findOneById(id: string, db = this.drizzle.db) {
|
||||||
return this.db.query.collections.findFirst({
|
return db.query.collections.findFirst({
|
||||||
where: eq(collections.id, id),
|
where: eq(collections.id, id),
|
||||||
columns: {
|
columns: {
|
||||||
cuid: true,
|
cuid: true,
|
||||||
|
|
@ -26,8 +25,8 @@ export class CollectionsRepository implements Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByCuid(cuid: string) {
|
async findOneByCuid(cuid: string, db = this.drizzle.db) {
|
||||||
return this.db.query.collections.findFirst({
|
return db.query.collections.findFirst({
|
||||||
where: eq(collections.cuid, cuid),
|
where: eq(collections.cuid, cuid),
|
||||||
columns: {
|
columns: {
|
||||||
cuid: true,
|
cuid: true,
|
||||||
|
|
@ -36,8 +35,8 @@ export class CollectionsRepository implements Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByUserId(userId: string) {
|
async findOneByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.collections.findFirst({
|
return db.query.collections.findFirst({
|
||||||
where: eq(collections.user_id, userId),
|
where: eq(collections.user_id, userId),
|
||||||
columns: {
|
columns: {
|
||||||
cuid: true,
|
cuid: true,
|
||||||
|
|
@ -46,21 +45,17 @@ export class CollectionsRepository implements Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAllByUserId(userId: string) {
|
async findAllByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.collections.findMany({
|
return db.query.collections.findMany({
|
||||||
where: eq(collections.user_id, userId),
|
where: eq(collections.user_id, userId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: CreateCollection) {
|
async create(data: CreateCollection, db = this.drizzle.db) {
|
||||||
return this.db.insert(collections).values(data).returning().then(takeFirstOrThrow)
|
return db.insert(collections).values(data).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: UpdateCollection) {
|
async update(id: string, data: UpdateCollection, db = this.drizzle.db) {
|
||||||
return this.db.update(collections).set(data).where(eq(collections.id, id)).returning().then(takeFirstOrThrow)
|
return db.update(collections).set(data).where(eq(collections.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
|
||||||
|
|
||||||
trxHost(trx: DatabaseProvider) {
|
|
||||||
return new CollectionsRepository(trx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,70 @@
|
||||||
import 'reflect-metadata'
|
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 { 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 { type InferInsertModel, and, eq } from 'drizzle-orm'
|
||||||
import { inject, injectable } from 'tsyringe'
|
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 CreateCredentials = InferInsertModel<typeof credentialsTable>
|
||||||
export type UpdateCredentials = Partial<CreateCredentials>
|
export type UpdateCredentials = Partial<CreateCredentials>
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CredentialsRepository implements Repository {
|
export class CredentialsRepository {
|
||||||
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
|
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
|
||||||
|
|
||||||
async findOneByUserId(userId: string) {
|
async findOneByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.credentialsTable.findFirst({
|
return db.query.credentialsTable.findFirst({
|
||||||
where: eq(credentialsTable.user_id, userId),
|
where: eq(credentialsTable.user_id, userId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByUserIdAndType(userId: string, type: CredentialsType) {
|
async findOneByUserIdAndType(userId: string, type: CredentialsType, db = this.drizzle.db) {
|
||||||
return this.db.query.credentialsTable.findFirst({
|
return db.query.credentialsTable.findFirst({
|
||||||
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)),
|
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findPasswordCredentialsByUserId(userId: string) {
|
async findPasswordCredentialsByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.credentialsTable.findFirst({
|
return db.query.credentialsTable.findFirst({
|
||||||
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.PASSWORD)),
|
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.PASSWORD)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findTOTPCredentialsByUserId(userId: string) {
|
async findTOTPCredentialsByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.credentialsTable.findFirst({
|
return db.query.credentialsTable.findFirst({
|
||||||
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.TOTP)),
|
where: and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, CredentialsType.TOTP)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneById(id: string) {
|
async findOneById(id: string, db = this.drizzle.db) {
|
||||||
return this.db.query.credentialsTable.findFirst({
|
return db.query.credentialsTable.findFirst({
|
||||||
where: eq(credentialsTable.id, id),
|
where: eq(credentialsTable.id, id),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByIdOrThrow(id: string) {
|
async findOneByIdOrThrow(id: string, db = this.drizzle.db) {
|
||||||
const credentials = await this.findOneById(id)
|
const credentials = await this.findOneById(id)
|
||||||
if (!credentials) throw Error('Credentials not found')
|
if (!credentials) throw Error('Credentials not found')
|
||||||
return credentials
|
return credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: CreateCredentials) {
|
async create(data: CreateCredentials, db = this.drizzle.db) {
|
||||||
return this.db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow)
|
return db.insert(credentialsTable).values(data).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: UpdateCredentials) {
|
async update(id: string, data: UpdateCredentials, db = this.drizzle.db) {
|
||||||
return this.db.update(credentialsTable).set(data).where(eq(credentialsTable.id, id)).returning().then(takeFirstOrThrow)
|
return db.update(credentialsTable).set(data).where(eq(credentialsTable.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string) {
|
async delete(id: string, db = this.drizzle.db) {
|
||||||
return this.db.delete(credentialsTable).where(eq(credentialsTable.id, id))
|
return db.delete(credentialsTable).where(eq(credentialsTable.id, id))
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteByUserId(userId: string) {
|
async deleteByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.delete(credentialsTable).where(eq(credentialsTable.user_id, userId))
|
return db.delete(credentialsTable).where(eq(credentialsTable.user_id, userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteByUserIdAndType(userId: string, type: CredentialsType) {
|
async deleteByUserIdAndType(userId: string, type: CredentialsType, db = this.drizzle.db) {
|
||||||
return this.db.delete(credentialsTable).where(and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)))
|
return db.delete(credentialsTable).where(and(eq(credentialsTable.user_id, userId), eq(credentialsTable.type, type)))
|
||||||
}
|
|
||||||
|
|
||||||
trxHost(trx: DatabaseProvider) {
|
|
||||||
return new CredentialsRepository(trx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
|
import { takeFirstOrThrow } from '$lib/server/api/common/utils/repository'
|
||||||
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 { type InferInsertModel, eq } from 'drizzle-orm'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { recoveryCodesTable } from '../databases/tables'
|
import { recoveryCodesTable } from '../databases/tables'
|
||||||
|
|
@ -8,20 +8,20 @@ import { recoveryCodesTable } from '../databases/tables'
|
||||||
export type CreateRecoveryCodes = InferInsertModel<typeof recoveryCodesTable>
|
export type CreateRecoveryCodes = InferInsertModel<typeof recoveryCodesTable>
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class RecoveryCodesRepository implements Repository {
|
export class RecoveryCodesRepository {
|
||||||
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
|
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
|
||||||
|
|
||||||
async findAllByUserId(userId: string) {
|
async create(data: CreateRecoveryCodes, db = this.drizzle.db) {
|
||||||
return this.db.query.recoveryCodesTable.findFirst({
|
return db.insert(recoveryCodesTable).values(data).returning().then(takeFirstOrThrow)
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAllByUserId(userId: string, db = this.drizzle.db) {
|
||||||
|
return db.query.recoveryCodesTable.findFirst({
|
||||||
where: eq(recoveryCodesTable.userId, userId),
|
where: eq(recoveryCodesTable.userId, userId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAllByUserId(userId: string) {
|
async deleteAllByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.delete(recoveryCodesTable).where(eq(recoveryCodesTable.userId, userId))
|
return db.delete(recoveryCodesTable).where(eq(recoveryCodesTable.userId, userId))
|
||||||
}
|
|
||||||
|
|
||||||
trxHost(trx: DatabaseProvider) {
|
|
||||||
return new RecoveryCodesRepository(trx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
|
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
|
||||||
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
|
|
||||||
import { type InferInsertModel, eq } from 'drizzle-orm'
|
import { type InferInsertModel, eq } from 'drizzle-orm'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { takeFirstOrThrow } from '../common/utils/repository.utils'
|
import { takeFirstOrThrow } from '../common/utils/repository'
|
||||||
import { roles } from '../databases/tables'
|
import { rolesTable } from '../databases/tables'
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Repository */
|
/* 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.
|
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>
|
export type UpdateRole = Partial<CreateRole>
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class RolesRepository implements Repository {
|
export class RolesRepository {
|
||||||
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
|
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
|
||||||
|
|
||||||
async findOneById(id: string) {
|
async findOneById(id: string, db = this.drizzle.db) {
|
||||||
return this.db.query.roles.findFirst({
|
return db.query.roles.findFirst({
|
||||||
where: eq(roles.id, id),
|
where: eq(rolesTable.id, id),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByIdOrThrow(id: string) {
|
async findOneByIdOrThrow(id: string, db = this.drizzle.db) {
|
||||||
const role = await this.findOneById(id)
|
const role = await this.findOneById(id)
|
||||||
if (!role) throw Error('Role not found')
|
if (!role) throw Error('Role not found')
|
||||||
return role
|
return role
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll() {
|
async findAll(db = this.drizzle.db) {
|
||||||
return this.db.query.roles.findMany()
|
return db.query.roles.findMany()
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByName(name: string) {
|
async findOneByName(name: string, db = this.drizzle.db) {
|
||||||
return this.db.query.roles.findFirst({
|
return db.query.roles.findFirst({
|
||||||
where: eq(roles.name, name),
|
where: eq(rolesTable.name, name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByNameOrThrow(name: string) {
|
async findOneByNameOrThrow(name: string, db = this.drizzle.db) {
|
||||||
const role = await this.findOneByName(name)
|
const role = await this.findOneByName(name)
|
||||||
if (!role) throw Error('Role not found')
|
if (!role) throw Error('Role not found')
|
||||||
return role
|
return role
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: CreateRole) {
|
async create(data: CreateRole, db = this.drizzle.db) {
|
||||||
return this.db.insert(roles).values(data).returning().then(takeFirstOrThrow)
|
return db.insert(rolesTable).values(data).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: UpdateRole) {
|
async update(id: string, data: UpdateRole, db = this.drizzle.db) {
|
||||||
return this.db.update(roles).set(data).where(eq(roles.id, id)).returning().then(takeFirstOrThrow)
|
return db.update(rolesTable).set(data).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string) {
|
async delete(id: string, db = this.drizzle.db) {
|
||||||
return this.db.delete(roles).where(eq(roles.id, id)).returning().then(takeFirstOrThrow)
|
return db.delete(rolesTable).where(eq(rolesTable.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
|
||||||
|
|
||||||
trxHost(trx: DatabaseProvider) {
|
|
||||||
return new RolesRepository(trx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
|
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
|
||||||
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
|
|
||||||
import { type InferInsertModel, eq } from 'drizzle-orm'
|
import { type InferInsertModel, eq } from 'drizzle-orm'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { takeFirstOrThrow } from '../common/utils/repository.utils'
|
import { takeFirstOrThrow } from '../common/utils/repository'
|
||||||
import { user_roles } from '../databases/tables'
|
import { user_roles } from '../databases/tables'
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
@ -25,11 +24,11 @@ export type CreateUserRole = InferInsertModel<typeof user_roles>
|
||||||
export type UpdateUserRole = Partial<CreateUserRole>
|
export type UpdateUserRole = Partial<CreateUserRole>
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class UserRolesRepository implements Repository {
|
export class UserRolesRepository {
|
||||||
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
|
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
|
||||||
|
|
||||||
async findOneById(id: string) {
|
async findOneById(id: string, db = this.drizzle.db) {
|
||||||
return this.db.query.user_roles.findFirst({
|
return db.query.user_roles.findFirst({
|
||||||
where: eq(user_roles.id, id),
|
where: eq(user_roles.id, id),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -40,21 +39,17 @@ export class UserRolesRepository implements Repository {
|
||||||
return userRole
|
return userRole
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAllByUserId(userId: string) {
|
async findAllByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.user_roles.findMany({
|
return db.query.user_roles.findMany({
|
||||||
where: eq(user_roles.user_id, userId),
|
where: eq(user_roles.user_id, userId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: CreateUserRole) {
|
async create(data: CreateUserRole, db = this.drizzle.db) {
|
||||||
return this.db.insert(user_roles).values(data).returning().then(takeFirstOrThrow)
|
return db.insert(user_roles).values(data).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string) {
|
async delete(id: string, db = this.drizzle.db) {
|
||||||
return this.db.delete(user_roles).where(eq(user_roles.id, id)).returning().then(takeFirstOrThrow)
|
return db.delete(user_roles).where(eq(user_roles.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
|
||||||
|
|
||||||
trxHost(trx: DatabaseProvider) {
|
|
||||||
return new UserRolesRepository(trx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { 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 { type InferInsertModel, eq } from 'drizzle-orm'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { takeFirstOrThrow } from '../common/utils/repository.utils'
|
import { takeFirstOrThrow } from '../common/utils/repository'
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Repository */
|
/* Repository */
|
||||||
|
|
@ -25,46 +24,42 @@ export type CreateUser = InferInsertModel<typeof usersTable>
|
||||||
export type UpdateUser = Partial<CreateUser>
|
export type UpdateUser = Partial<CreateUser>
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class UsersRepository implements Repository {
|
export class UsersRepository {
|
||||||
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
|
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
|
||||||
|
|
||||||
async findOneById(id: string) {
|
async findOneById(id: string, db = this.drizzle.db) {
|
||||||
return this.db.query.usersTable.findFirst({
|
return db.query.usersTable.findFirst({
|
||||||
where: eq(usersTable.id, id),
|
where: eq(usersTable.id, id),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByIdOrThrow(id: string) {
|
async findOneByIdOrThrow(id: string, db = this.drizzle.db) {
|
||||||
const user = await this.findOneById(id)
|
const user = await this.findOneById(id)
|
||||||
if (!user) throw Error('User not found')
|
if (!user) throw Error('User not found')
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByUsername(username: string) {
|
async findOneByUsername(username: string, db = this.drizzle.db) {
|
||||||
return this.db.query.usersTable.findFirst({
|
return db.query.usersTable.findFirst({
|
||||||
where: eq(usersTable.username, username),
|
where: eq(usersTable.username, username),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByEmail(email: string) {
|
async findOneByEmail(email: string, db = this.drizzle.db) {
|
||||||
return this.db.query.usersTable.findFirst({
|
return db.query.usersTable.findFirst({
|
||||||
where: eq(usersTable.email, email),
|
where: eq(usersTable.email, email),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: CreateUser) {
|
async create(data: CreateUser, db = this.drizzle.db) {
|
||||||
return this.db.insert(usersTable).values(data).returning().then(takeFirstOrThrow)
|
return db.insert(usersTable).values(data).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: UpdateUser) {
|
async update(id: string, data: UpdateUser, db = this.drizzle.db) {
|
||||||
return this.db.update(usersTable).set(data).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
|
return db.update(usersTable).set(data).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string) {
|
async delete(id: string, db = this.drizzle.db) {
|
||||||
return this.db.delete(usersTable).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
|
return db.delete(usersTable).where(eq(usersTable.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
|
||||||
|
|
||||||
trxHost(trx: DatabaseProvider) {
|
|
||||||
return new UsersRepository(trx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
import type { Repository } from '$lib/server/api/common/interfaces/repository.interface'
|
import { DrizzleService } from '$lib/server/api/services/drizzle.service'
|
||||||
import { DatabaseProvider } from '$lib/server/api/providers/database.provider'
|
|
||||||
import { type InferInsertModel, eq } from 'drizzle-orm'
|
import { type InferInsertModel, eq } from 'drizzle-orm'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { takeFirstOrThrow } from '../common/utils/repository.utils'
|
import { takeFirstOrThrow } from '../common/utils/repository'
|
||||||
import { wishlists } from '../databases/tables'
|
import { wishlistsTable } from '../databases/tables'
|
||||||
|
|
||||||
export type CreateWishlist = InferInsertModel<typeof wishlists>
|
export type CreateWishlist = InferInsertModel<typeof wishlistsTable>
|
||||||
export type UpdateWishlist = Partial<CreateWishlist>
|
export type UpdateWishlist = Partial<CreateWishlist>
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class WishlistsRepository implements Repository {
|
export class WishlistsRepository {
|
||||||
constructor(@inject(DatabaseProvider) private readonly db: DatabaseProvider) {}
|
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {}
|
||||||
|
|
||||||
async findAll() {
|
async findAll(db = this.drizzle.db) {
|
||||||
return this.db.query.wishlists.findMany()
|
return db.query.wishlistsTable.findMany()
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneById(id: string) {
|
async findOneById(id: string, db = this.drizzle.db) {
|
||||||
return this.db.query.wishlists.findFirst({
|
return db.query.wishlistsTable.findFirst({
|
||||||
where: eq(wishlists.id, id),
|
where: eq(wishlistsTable.id, id),
|
||||||
columns: {
|
columns: {
|
||||||
cuid: true,
|
cuid: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
|
@ -26,9 +25,9 @@ export class WishlistsRepository implements Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByCuid(cuid: string) {
|
async findOneByCuid(cuid: string, db = this.drizzle.db) {
|
||||||
return this.db.query.wishlists.findFirst({
|
return db.query.wishlistsTable.findFirst({
|
||||||
where: eq(wishlists.cuid, cuid),
|
where: eq(wishlistsTable.cuid, cuid),
|
||||||
columns: {
|
columns: {
|
||||||
cuid: true,
|
cuid: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
|
@ -36,9 +35,9 @@ export class WishlistsRepository implements Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOneByUserId(userId: string) {
|
async findOneByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.wishlists.findFirst({
|
return db.query.wishlistsTable.findFirst({
|
||||||
where: eq(wishlists.user_id, userId),
|
where: eq(wishlistsTable.user_id, userId),
|
||||||
columns: {
|
columns: {
|
||||||
cuid: true,
|
cuid: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
|
@ -46,9 +45,9 @@ export class WishlistsRepository implements Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAllByUserId(userId: string) {
|
async findAllByUserId(userId: string, db = this.drizzle.db) {
|
||||||
return this.db.query.wishlists.findMany({
|
return db.query.wishlistsTable.findMany({
|
||||||
where: eq(wishlists.user_id, userId),
|
where: eq(wishlistsTable.user_id, userId),
|
||||||
columns: {
|
columns: {
|
||||||
cuid: true,
|
cuid: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
|
@ -56,15 +55,11 @@ export class WishlistsRepository implements Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: CreateWishlist) {
|
async create(data: CreateWishlist, db = this.drizzle.db) {
|
||||||
return this.db.insert(wishlists).values(data).returning().then(takeFirstOrThrow)
|
return db.insert(wishlistsTable).values(data).returning().then(takeFirstOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, data: UpdateWishlist) {
|
async update(id: string, data: UpdateWishlist, db = this.drizzle.db) {
|
||||||
return this.db.update(wishlists).set(data).where(eq(wishlists.id, id)).returning().then(takeFirstOrThrow)
|
return db.update(wishlistsTable).set(data).where(eq(wishlistsTable.id, id)).returning().then(takeFirstOrThrow)
|
||||||
}
|
|
||||||
|
|
||||||
trxHost(trx: DatabaseProvider) {
|
|
||||||
return new WishlistsRepository(trx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
src/lib/server/api/services/drizzle.service.ts
Normal file
33
src/lib/server/api/services/drizzle.service.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { config } from '$lib/server/api/common/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.postgres.user,
|
||||||
|
password: config.postgres.password,
|
||||||
|
host: config.postgres.host,
|
||||||
|
port: Number(config.postgres.port).valueOf(),
|
||||||
|
database: config.postgres.database,
|
||||||
|
ssl: config.postgres.ssl,
|
||||||
|
max: config.postgres.max,
|
||||||
|
})
|
||||||
|
this.pool = pool
|
||||||
|
this.db = drizzle(pool, {
|
||||||
|
schema,
|
||||||
|
logger: process.env.NODE_ENV === 'development',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): Promise<void> | void {
|
||||||
|
this.pool.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { UpdateEmailDto } from '$lib/server/api/dtos/update-email.dto'
|
import type { UpdateEmailDto } from '$lib/server/api/dtos/update-email.dto'
|
||||||
import type { UpdateProfileDto } from '$lib/server/api/dtos/update-profile.dto'
|
import type { UpdateProfileDto } from '$lib/server/api/dtos/update-profile.dto'
|
||||||
import type { VerifyPasswordDto } from '$lib/server/api/dtos/verify-password.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 { UsersService } from '$lib/server/api/services/users.service'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
|
|
||||||
|
|
@ -25,12 +25,12 @@ simple as possible. This makes the service easier to read, test and understand.
|
||||||
@injectable()
|
@injectable()
|
||||||
export class IamService {
|
export class IamService {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(LuciaProvider) private readonly lucia: LuciaProvider,
|
@inject(LuciaService) private luciaService: LuciaService,
|
||||||
@inject(UsersService) private readonly usersService: UsersService,
|
@inject(UsersService) private readonly usersService: UsersService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async logout(sessionId: string) {
|
async logout(sessionId: string) {
|
||||||
return this.lucia.invalidateSession(sessionId)
|
return this.luciaService.lucia.invalidateSession(sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProfile(userId: string, data: UpdateProfileDto) {
|
async updateProfile(userId: string, data: UpdateProfileDto) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type { SigninUsernameDto } from '$lib/server/api/dtos/signin-username.dto'
|
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 type { HonoRequest } from 'hono'
|
||||||
import { inject, injectable } from 'tsyringe'
|
import { inject, injectable } from 'tsyringe'
|
||||||
import { BadRequest } from '../common/exceptions'
|
import { BadRequest } from '../common/exceptions'
|
||||||
import type { Credentials } from '../databases/tables'
|
import type { Credentials } from '../databases/tables'
|
||||||
import { DatabaseProvider } from '../providers/database.provider'
|
import { DatabaseProvider } from '../providers/database.provider'
|
||||||
import { LuciaProvider } from '../providers/lucia.provider'
|
|
||||||
import { CredentialsRepository } from '../repositories/credentials.repository'
|
import { CredentialsRepository } from '../repositories/credentials.repository'
|
||||||
import { UsersRepository } from '../repositories/users.repository'
|
import { UsersRepository } from '../repositories/users.repository'
|
||||||
import { MailerService } from './mailer.service'
|
import { MailerService } from './mailer.service'
|
||||||
|
|
@ -13,7 +13,7 @@ import { TokensService } from './tokens.service'
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LoginRequestsService {
|
export class LoginRequestsService {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(LuciaProvider) private readonly lucia: LuciaProvider,
|
@inject(LuciaService) private luciaService: LuciaService,
|
||||||
@inject(DatabaseProvider) private readonly db: DatabaseProvider,
|
@inject(DatabaseProvider) private readonly db: DatabaseProvider,
|
||||||
@inject(TokensService) private readonly tokensService: TokensService,
|
@inject(TokensService) private readonly tokensService: TokensService,
|
||||||
@inject(MailerService) private readonly mailerService: MailerService,
|
@inject(MailerService) private readonly mailerService: MailerService,
|
||||||
|
|
@ -60,7 +60,7 @@ export class LoginRequestsService {
|
||||||
async createUserSession(existingUserId: string, req: HonoRequest, totpCredentials: Credentials | undefined) {
|
async createUserSession(existingUserId: string, req: HonoRequest, totpCredentials: Credentials | undefined) {
|
||||||
const requestIpAddress = req.header('x-real-ip')
|
const requestIpAddress = req.header('x-real-ip')
|
||||||
const requestIpCountry = req.header('x-vercel-ip-country')
|
const requestIpCountry = req.header('x-vercel-ip-country')
|
||||||
return this.lucia.createSession(existingUserId, {
|
return this.luciaService.lucia.createSession(existingUserId, {
|
||||||
ip_country: requestIpCountry || 'unknown',
|
ip_country: requestIpCountry || 'unknown',
|
||||||
ip_address: requestIpAddress || 'unknown',
|
ip_address: requestIpAddress || 'unknown',
|
||||||
twoFactorAuthEnabled: !!totpCredentials && totpCredentials?.secret_data !== null && totpCredentials?.secret_data !== '',
|
twoFactorAuthEnabled: !!totpCredentials && totpCredentials?.secret_data !== null && totpCredentials?.secret_data !== '',
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue