mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Deleting all the old DB folder data and slowly migrating each page.
This commit is contained in:
parent
eeca4e4103
commit
60d0706d58
59 changed files with 291 additions and 7103 deletions
|
|
@ -1,24 +0,0 @@
|
|||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import pg from 'pg';
|
||||
import env from '../env';
|
||||
import * as schema from './schema';
|
||||
|
||||
// create the connection
|
||||
export const pool = new pg.Pool({
|
||||
user: env.DATABASE_USER,
|
||||
password: env.DATABASE_PASSWORD,
|
||||
host: env.DATABASE_HOST,
|
||||
port: Number(env.DATABASE_PORT).valueOf(),
|
||||
database: env.DATABASE_DB,
|
||||
ssl: env.DATABASE_HOST !== 'localhost',
|
||||
max: env.DB_MIGRATING || env.DB_SEEDING ? 1 : undefined,
|
||||
});
|
||||
|
||||
export const db = drizzle(pool, {
|
||||
schema,
|
||||
logger: env.NODE_ENV === 'development',
|
||||
});
|
||||
|
||||
export type db = typeof db;
|
||||
|
||||
export default db;
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import 'dotenv/config';
|
||||
import postgres from 'postgres';
|
||||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import { migrate } from 'drizzle-orm/postgres-js/migrator';
|
||||
import env from '../env';
|
||||
import config from '../../drizzle.config';
|
||||
|
||||
const connection = postgres({
|
||||
host: env.DATABASE_HOST || 'localhost',
|
||||
port: env.DATABASE_PORT,
|
||||
user: env.DATABASE_USER || 'root',
|
||||
password: env.DATABASE_PASSWORD || '',
|
||||
database: env.DATABASE_DB || 'boredgame',
|
||||
ssl: env.NODE_ENV === 'development' ? false : 'require',
|
||||
max: 1,
|
||||
});
|
||||
const db = drizzle(connection);
|
||||
|
||||
try {
|
||||
await migrate(db, { migrationsFolder: config.out! });
|
||||
console.log('Migrations complete');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
process.exit();
|
||||
|
|
@ -1,410 +0,0 @@
|
|||
DO $$ BEGIN
|
||||
CREATE TYPE "public"."external_id_type" AS ENUM('game', 'category', 'mechanic', 'publisher', 'designer', 'artist');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "categories" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"name" text,
|
||||
"slug" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "categories_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "categories_to_external_ids" (
|
||||
"category_id" uuid NOT NULL,
|
||||
"external_id" uuid NOT NULL,
|
||||
CONSTRAINT "categories_to_external_ids_category_id_external_id_pk" PRIMARY KEY("category_id","external_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "categories_to_games" (
|
||||
"category_id" uuid NOT NULL,
|
||||
"game_id" uuid NOT NULL,
|
||||
CONSTRAINT "categories_to_games_category_id_game_id_pk" PRIMARY KEY("category_id","game_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "collection_items" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"collection_id" uuid NOT NULL,
|
||||
"game_id" uuid NOT NULL,
|
||||
"times_played" integer DEFAULT 0,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "collection_items_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "collections" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"user_id" uuid NOT NULL,
|
||||
"name" text DEFAULT 'My Collection' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "collections_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "expansions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"base_game_id" uuid NOT NULL,
|
||||
"game_id" uuid NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "expansions_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "external_ids" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"type" "external_id_type",
|
||||
"external_id" text NOT NULL,
|
||||
CONSTRAINT "external_ids_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "games" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"name" text NOT NULL,
|
||||
"slug" text NOT NULL,
|
||||
"description" text,
|
||||
"year_published" integer,
|
||||
"min_players" integer,
|
||||
"max_players" integer,
|
||||
"playtime" integer,
|
||||
"min_playtime" integer,
|
||||
"max_playtime" integer,
|
||||
"min_age" integer,
|
||||
"image_url" text,
|
||||
"thumb_url" text,
|
||||
"url" text,
|
||||
"last_sync_at" timestamp,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "games_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "games_to_external_ids" (
|
||||
"game_id" uuid NOT NULL,
|
||||
"external_id" uuid NOT NULL,
|
||||
CONSTRAINT "games_to_external_ids_game_id_external_id_pk" PRIMARY KEY("game_id","external_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "mechanics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"name" text,
|
||||
"slug" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "mechanics_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "mechanics_to_external_ids" (
|
||||
"mechanic_id" uuid NOT NULL,
|
||||
"external_id" uuid NOT NULL,
|
||||
CONSTRAINT "mechanics_to_external_ids_mechanic_id_external_id_pk" PRIMARY KEY("mechanic_id","external_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "mechanics_to_games" (
|
||||
"mechanic_id" uuid NOT NULL,
|
||||
"game_id" uuid NOT NULL,
|
||||
CONSTRAINT "mechanics_to_games_mechanic_id_game_id_pk" PRIMARY KEY("mechanic_id","game_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "password_reset_tokens" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"expires_at" timestamp,
|
||||
"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 "publishers" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"name" text,
|
||||
"slug" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "publishers_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "publishers_to_external_ids" (
|
||||
"publisher_id" uuid NOT NULL,
|
||||
"external_id" uuid NOT NULL,
|
||||
CONSTRAINT "publishers_to_external_ids_publisher_id_external_id_pk" PRIMARY KEY("publisher_id","external_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "publishers_to_games" (
|
||||
"publisher_id" uuid NOT NULL,
|
||||
"game_id" uuid NOT NULL,
|
||||
CONSTRAINT "publishers_to_games_publisher_id_game_id_pk" PRIMARY KEY("publisher_id","game_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "recovery_codes" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"code" text NOT NULL,
|
||||
"used" boolean DEFAULT false,
|
||||
"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 "roles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "roles_cuid_unique" UNIQUE("cuid"),
|
||||
CONSTRAINT "roles_name_unique" UNIQUE("name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "sessions" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL,
|
||||
"ip_country" text,
|
||||
"ip_address" text,
|
||||
"two_factor_auth_enabled" boolean DEFAULT false,
|
||||
"is_two_factor_authenticated" boolean DEFAULT false
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "two_factor" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"two_factor_secret" text NOT NULL,
|
||||
"two_factor_enabled" boolean DEFAULT false NOT NULL,
|
||||
"initiated_time" timestamp with time zone NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "two_factor_cuid_unique" UNIQUE("cuid"),
|
||||
CONSTRAINT "two_factor_user_id_unique" UNIQUE("user_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "user_roles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"user_id" uuid NOT NULL,
|
||||
"role_id" uuid NOT NULL,
|
||||
"primary" boolean DEFAULT false,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "user_roles_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "users" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"username" text,
|
||||
"hashed_password" text,
|
||||
"email" text,
|
||||
"first_name" text,
|
||||
"last_name" text,
|
||||
"verified" boolean DEFAULT false,
|
||||
"receive_email" boolean DEFAULT false,
|
||||
"theme" text DEFAULT 'system',
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "users_cuid_unique" UNIQUE("cuid"),
|
||||
CONSTRAINT "users_username_unique" UNIQUE("username"),
|
||||
CONSTRAINT "users_email_unique" UNIQUE("email")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "wishlist_items" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"wishlist_id" uuid NOT NULL,
|
||||
"game_id" uuid NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "wishlist_items_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "wishlists" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"cuid" text,
|
||||
"user_id" uuid NOT NULL,
|
||||
"name" text DEFAULT 'My Wishlist' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "wishlists_cuid_unique" UNIQUE("cuid")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "categories_to_external_ids" ADD CONSTRAINT "categories_to_external_ids_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "categories_to_external_ids" ADD CONSTRAINT "categories_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "public"."external_ids"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "categories_to_games" ADD CONSTRAINT "categories_to_games_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "categories_to_games" ADD CONSTRAINT "categories_to_games_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "collection_items" ADD CONSTRAINT "collection_items_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "collection_items" ADD CONSTRAINT "collection_items_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "collections" ADD CONSTRAINT "collections_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "expansions" ADD CONSTRAINT "expansions_base_game_id_games_id_fk" FOREIGN KEY ("base_game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "expansions" ADD CONSTRAINT "expansions_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "games_to_external_ids" ADD CONSTRAINT "games_to_external_ids_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "games_to_external_ids" ADD CONSTRAINT "games_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "public"."external_ids"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "mechanics_to_external_ids" ADD CONSTRAINT "mechanics_to_external_ids_mechanic_id_mechanics_id_fk" FOREIGN KEY ("mechanic_id") REFERENCES "public"."mechanics"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "mechanics_to_external_ids" ADD CONSTRAINT "mechanics_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "public"."external_ids"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "mechanics_to_games" ADD CONSTRAINT "mechanics_to_games_mechanic_id_mechanics_id_fk" FOREIGN KEY ("mechanic_id") REFERENCES "public"."mechanics"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "mechanics_to_games" ADD CONSTRAINT "mechanics_to_games_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_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 "publishers_to_external_ids" ADD CONSTRAINT "publishers_to_external_ids_publisher_id_publishers_id_fk" FOREIGN KEY ("publisher_id") REFERENCES "public"."publishers"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "publishers_to_external_ids" ADD CONSTRAINT "publishers_to_external_ids_external_id_external_ids_id_fk" FOREIGN KEY ("external_id") REFERENCES "public"."external_ids"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "publishers_to_games" ADD CONSTRAINT "publishers_to_games_publisher_id_publishers_id_fk" FOREIGN KEY ("publisher_id") REFERENCES "public"."publishers"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "publishers_to_games" ADD CONSTRAINT "publishers_to_games_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE restrict ON UPDATE cascade;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "recovery_codes" ADD CONSTRAINT "recovery_codes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "two_factor" ADD CONSTRAINT "two_factor_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_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 "user_roles" ADD CONSTRAINT "user_roles_role_id_roles_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."roles"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "wishlist_items" ADD CONSTRAINT "wishlist_items_wishlist_id_wishlists_id_fk" FOREIGN KEY ("wishlist_id") REFERENCES "public"."wishlists"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "wishlist_items" ADD CONSTRAINT "wishlist_items_game_id_games_id_fk" FOREIGN KEY ("game_id") REFERENCES "public"."games"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "wishlists" ADD CONSTRAINT "wishlists_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
|
||||
CREATE INDEX IF NOT EXISTS "search_index" ON "games" USING gin ((
|
||||
setweight(to_tsvector('english', "name"), 'A') ||
|
||||
setweight(to_tsvector('english', "slug"), 'B')
|
||||
));
|
||||
|
|
@ -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;
|
||||
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
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1720625651245,
|
||||
"tag": "0000_dazzling_stick",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1720625948784,
|
||||
"tag": "0001_noisy_sally_floyd",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "7",
|
||||
"when": 1720626020902,
|
||||
"tag": "0002_fancy_valkyrie",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import categoriesToExternalIds from './categoriesToExternalIds';
|
||||
import categories_to_games from './categoriesToGames';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const categories = pgTable('categories', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
name: text('name'),
|
||||
slug: text('slug'),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export type Categories = InferSelectModel<typeof categories>;
|
||||
|
||||
export const categories_relations = relations(categories, ({ many }) => ({
|
||||
categories_to_games: many(categories_to_games),
|
||||
categoriesToExternalIds: many(categoriesToExternalIds),
|
||||
}));
|
||||
|
||||
export default categories;
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||
import categories from './categories';
|
||||
import externalIds from './externalIds';
|
||||
import { relations } from 'drizzle-orm';
|
||||
|
||||
const categoriesToExternalIds = pgTable(
|
||||
'categories_to_external_ids',
|
||||
{
|
||||
categoryId: uuid('category_id')
|
||||
.notNull()
|
||||
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
externalId: uuid('external_id')
|
||||
.notNull()
|
||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
categoriesToExternalIdsPkey: primaryKey({
|
||||
columns: [table.categoryId, table.externalId],
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const categoriesToExternalIdsRelations = relations(
|
||||
categoriesToExternalIds,
|
||||
({ one }) => ({
|
||||
category: one(categories, {
|
||||
fields: [categoriesToExternalIds.categoryId],
|
||||
references: [categories.id],
|
||||
}),
|
||||
externalId: one(externalIds, {
|
||||
fields: [categoriesToExternalIds.externalId],
|
||||
references: [externalIds.id],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export default categoriesToExternalIds;
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import categories from './categories';
|
||||
import games from './games';
|
||||
|
||||
const categories_to_games = pgTable(
|
||||
'categories_to_games',
|
||||
{
|
||||
category_id: uuid('category_id')
|
||||
.notNull()
|
||||
.references(() => categories.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
game_id: uuid('game_id')
|
||||
.notNull()
|
||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
categoriesToGamesPkey: primaryKey({
|
||||
columns: [table.category_id, table.game_id],
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const categories_to_games_relations = relations(categories_to_games, ({ one }) => ({
|
||||
category: one(categories, {
|
||||
fields: [categories_to_games.category_id],
|
||||
references: [categories.id],
|
||||
}),
|
||||
game: one(games, {
|
||||
fields: [categories_to_games.game_id],
|
||||
references: [games.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default categories_to_games;
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { integer, pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import collections from './collections';
|
||||
import games from './games';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const collection_items = pgTable('collection_items', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
collection_id: uuid('collection_id')
|
||||
.notNull()
|
||||
.references(() => collections.id, { onDelete: 'cascade' }),
|
||||
game_id: uuid('game_id')
|
||||
.notNull()
|
||||
.references(() => games.id, { onDelete: 'cascade' }),
|
||||
times_played: integer('times_played').default(0),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export type CollectionItems = InferSelectModel<typeof collection_items>;
|
||||
|
||||
export const collection_item_relations = relations(collection_items, ({ one }) => ({
|
||||
collection: one(collections, {
|
||||
fields: [collection_items.collection_id],
|
||||
references: [collections.id],
|
||||
}),
|
||||
game: one(games, {
|
||||
fields: [collection_items.game_id],
|
||||
references: [games.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default collection_items;
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import usersTable from './users.table';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const collections = pgTable('collections', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
user_id: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull().default('My Collection'),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export const collection_relations = relations(collections, ({ one }) => ({
|
||||
user: one(usersTable, {
|
||||
fields: [collections.user_id],
|
||||
references: [usersTable.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type Collections = InferSelectModel<typeof collections>;
|
||||
|
||||
export default collections;
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import games from './games';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
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],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default expansions;
|
||||
|
|
@ -1,25 +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',
|
||||
]);
|
||||
|
||||
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>;
|
||||
|
||||
export default externalIds;
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import { index, integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations, sql } from 'drizzle-orm';
|
||||
import categoriesToGames from './categoriesToGames';
|
||||
import gamesToExternalIds from './gamesToExternalIds';
|
||||
import mechanicsToGames from './mechanicsToGames';
|
||||
import publishersToGames from './publishersToGames';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const games = pgTable(
|
||||
'games',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
name: text('name').notNull(),
|
||||
slug: text('slug').notNull(),
|
||||
description: text('description'),
|
||||
year_published: integer('year_published'),
|
||||
min_players: integer('min_players'),
|
||||
max_players: integer('max_players'),
|
||||
playtime: integer('playtime'),
|
||||
min_playtime: integer('min_playtime'),
|
||||
max_playtime: integer('max_playtime'),
|
||||
min_age: integer('min_age'),
|
||||
image_url: text('image_url'),
|
||||
thumb_url: text('thumb_url'),
|
||||
url: text('url'),
|
||||
last_sync_at: timestamp('last_sync_at'),
|
||||
...timestamps,
|
||||
},
|
||||
(table) => ({
|
||||
searchIndex: index('search_index').using(
|
||||
'gin',
|
||||
sql`(
|
||||
setweight(to_tsvector('english', ${table.name}), 'A') ||
|
||||
setweight(to_tsvector('english', ${table.slug}), 'B')
|
||||
)`,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const gameRelations = relations(games, ({ many }) => ({
|
||||
categories_to_games: many(categoriesToGames),
|
||||
mechanics_to_games: many(mechanicsToGames),
|
||||
publishers_to_games: many(publishersToGames),
|
||||
gamesToExternalIds: many(gamesToExternalIds),
|
||||
}));
|
||||
|
||||
export type Games = InferSelectModel<typeof games>;
|
||||
|
||||
export default games;
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||
import games from './games';
|
||||
import externalIds from './externalIds';
|
||||
|
||||
const gamesToExternalIds = pgTable(
|
||||
'games_to_external_ids',
|
||||
{
|
||||
gameId: uuid('game_id')
|
||||
.notNull()
|
||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
externalId: uuid('external_id')
|
||||
.notNull()
|
||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
gamesToExternalIdsPkey: primaryKey({
|
||||
columns: [table.gameId, table.externalId],
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default gamesToExternalIds;
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
export { default as usersTable, userRelations as user_relations, type Users } from './users.table';
|
||||
export { default as recoveryCodes, type RecoveryCodes } from './recoveryCodes';
|
||||
export {
|
||||
default as password_reset_tokens,
|
||||
password_reset_token_relations,
|
||||
type PasswordResetTokens,
|
||||
} from './passwordResetTokens';
|
||||
export { default as sessionsTable, type Sessions } from './sessions.table';
|
||||
export { default as roles, role_relations, type Roles } from './roles';
|
||||
export { default as userRoles, user_role_relations, type UserRoles } from './userRoles';
|
||||
export { default as collections, collection_relations, type Collections } from './collections';
|
||||
export {
|
||||
default as collection_items,
|
||||
collection_item_relations,
|
||||
type CollectionItems,
|
||||
} from './collectionItems';
|
||||
export { default as wishlists, wishlists_relations, type Wishlists } from './wishlists';
|
||||
export {
|
||||
default as wishlist_items,
|
||||
wishlist_item_relations,
|
||||
type WishlistItems,
|
||||
} from './wishlistItems';
|
||||
export { default as externalIds, type ExternalIds, externalIdType } from './externalIds';
|
||||
export { default as games, gameRelations, type Games } from './games';
|
||||
export { default as gamesToExternalIds } from './gamesToExternalIds';
|
||||
export { default as expansions, expansion_relations, type Expansions } from './expansions';
|
||||
export { default as publishers, publishers_relations, type Publishers } from './publishers';
|
||||
export { default as publishers_to_games, publishers_to_games_relations } from './publishersToGames';
|
||||
export { default as publishersToExternalIds } from './publishersToExternalIds';
|
||||
export { default as categories, categories_relations, type Categories } from './categories';
|
||||
export { default as categoriesToExternalIds } from './categoriesToExternalIds';
|
||||
export { default as categories_to_games, categories_to_games_relations } from './categoriesToGames';
|
||||
export { default as mechanics, mechanics_relations, type Mechanics } from './mechanics';
|
||||
export { default as mechanicsToExternalIds } from './mechanicsToExternalIds';
|
||||
export { default as mechanics_to_games, mechanics_to_games_relations } from './mechanicsToGames';
|
||||
export { default as twoFactor } from './two-factor.table';
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import mechanicsToGames from './mechanicsToGames';
|
||||
import mechanicsToExternalIds from './mechanicsToExternalIds';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
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(mechanicsToGames),
|
||||
mechanicsToExternalIds: many(mechanicsToExternalIds),
|
||||
}));
|
||||
|
||||
export default mechanics;
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||
import mechanics from './mechanics';
|
||||
import externalIds from './externalIds';
|
||||
|
||||
const mechanicsToExternalIds = pgTable(
|
||||
'mechanics_to_external_ids',
|
||||
{
|
||||
mechanicId: uuid('mechanic_id')
|
||||
.notNull()
|
||||
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
externalId: uuid('external_id')
|
||||
.notNull()
|
||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
mechanicsToExternalIdsPkey: primaryKey({
|
||||
columns: [table.mechanicId, table.externalId],
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default mechanicsToExternalIds;
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import mechanics from './mechanics';
|
||||
import games from './games';
|
||||
|
||||
const mechanics_to_games = pgTable(
|
||||
'mechanics_to_games',
|
||||
{
|
||||
mechanic_id: uuid('mechanic_id')
|
||||
.notNull()
|
||||
.references(() => mechanics.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
game_id: uuid('game_id')
|
||||
.notNull()
|
||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
mechanicsToGamesPkey: primaryKey({
|
||||
columns: [table.mechanic_id, table.game_id],
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const mechanics_to_games_relations = relations(mechanics_to_games, ({ one }) => ({
|
||||
mechanic: one(mechanics, {
|
||||
fields: [mechanics_to_games.mechanic_id],
|
||||
references: [mechanics.id],
|
||||
}),
|
||||
game: one(games, {
|
||||
fields: [mechanics_to_games.game_id],
|
||||
references: [games.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default mechanics_to_games;
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import usersTable from './users.table';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const password_reset_tokens = pgTable('password_reset_tokens', {
|
||||
id: text('id')
|
||||
.primaryKey()
|
||||
.$defaultFn(() => cuid2()),
|
||||
user_id: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
||||
expires_at: timestamp('expires_at'),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export type PasswordResetTokens = InferSelectModel<typeof password_reset_tokens>;
|
||||
|
||||
export const password_reset_token_relations = relations(password_reset_tokens, ({ one }) => ({
|
||||
user: one(usersTable, {
|
||||
fields: [password_reset_tokens.user_id],
|
||||
references: [usersTable.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default password_reset_tokens;
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import publishers_to_games from './publishersToGames';
|
||||
import publishersToExternalIds from './publishersToExternalIds';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
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),
|
||||
}));
|
||||
|
||||
export default publishers;
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||
import publishers from './publishers';
|
||||
import externalIds from './externalIds';
|
||||
|
||||
const publishersToExternalIds = pgTable(
|
||||
'publishers_to_external_ids',
|
||||
{
|
||||
publisherId: uuid('publisher_id')
|
||||
.notNull()
|
||||
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
externalId: uuid('external_id')
|
||||
.notNull()
|
||||
.references(() => externalIds.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
publishersToExternalIdsPkey: primaryKey({
|
||||
columns: [table.publisherId, table.externalId],
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default publishersToExternalIds;
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import publishers from './publishers';
|
||||
import games from './games';
|
||||
|
||||
const publishers_to_games = pgTable(
|
||||
'publishers_to_games',
|
||||
{
|
||||
publisher_id: uuid('publisher_id')
|
||||
.notNull()
|
||||
.references(() => publishers.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
game_id: uuid('game_id')
|
||||
.notNull()
|
||||
.references(() => games.id, { onDelete: 'restrict', onUpdate: 'cascade' }),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
publishersToGamesPkey: primaryKey({
|
||||
columns: [table.publisher_id, table.game_id],
|
||||
}),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const publishers_to_games_relations = relations(publishers_to_games, ({ one }) => ({
|
||||
publisher: one(publishers, {
|
||||
fields: [publishers_to_games.publisher_id],
|
||||
references: [publishers.id],
|
||||
}),
|
||||
game: one(games, {
|
||||
fields: [publishers_to_games.game_id],
|
||||
references: [games.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default publishers_to_games;
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import type { InferSelectModel } from 'drizzle-orm';
|
||||
import usersTable from './users.table';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const recovery_codes = pgTable('recovery_codes', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => usersTable.id),
|
||||
code: text('code').notNull(),
|
||||
used: boolean('used').default(false),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export type RecoveryCodes = InferSelectModel<typeof recovery_codes>;
|
||||
|
||||
export default recovery_codes;
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import user_roles from './userRoles';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const roles = pgTable('roles', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2())
|
||||
.notNull(),
|
||||
name: text('name').unique().notNull(),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export type Roles = InferSelectModel<typeof roles>;
|
||||
|
||||
export const role_relations = relations(roles, ({ many }) => ({
|
||||
user_roles: many(user_roles),
|
||||
}));
|
||||
|
||||
export default roles;
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||
import { relations, type InferSelectModel } from 'drizzle-orm';
|
||||
import usersTable from './users.table';
|
||||
|
||||
const sessionsTable = pgTable('sessions', {
|
||||
id: text('id').primaryKey(),
|
||||
userId: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => usersTable.id),
|
||||
expiresAt: timestamp('expires_at', {
|
||||
withTimezone: true,
|
||||
mode: 'date',
|
||||
}).notNull(),
|
||||
ipCountry: text('ip_country'),
|
||||
ipAddress: text('ip_address'),
|
||||
twoFactorAuthEnabled: boolean('two_factor_auth_enabled').default(false),
|
||||
isTwoFactorAuthenticated: boolean('is_two_factor_authenticated').default(false),
|
||||
});
|
||||
|
||||
export const sessionsRelations = relations(sessionsTable, ({ one }) => ({
|
||||
user: one(usersTable, {
|
||||
fields: [sessionsTable.userId],
|
||||
references: [usersTable.id],
|
||||
})
|
||||
}));
|
||||
|
||||
export type Sessions = InferSelectModel<typeof sessionsTable>;
|
||||
|
||||
export default sessionsTable;
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||
import { timestamps } from '../utils';
|
||||
import usersTable from './users.table';
|
||||
|
||||
const twoFactorTable = pgTable('two_factor', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
secret: text('secret').notNull(),
|
||||
enabled: boolean('enabled').notNull().default(false),
|
||||
initiatedTime: timestamp('initiated_time', {
|
||||
mode: 'date',
|
||||
withTimezone: true,
|
||||
}),
|
||||
userId: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => usersTable.id)
|
||||
.unique(),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export const emailVerificationsRelations = relations(twoFactorTable, ({ one }) => ({
|
||||
user: one(usersTable, {
|
||||
fields: [twoFactorTable.userId],
|
||||
references: [usersTable.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type TwoFactor = InferSelectModel<typeof twoFactorTable>;
|
||||
|
||||
export default twoFactorTable;
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import usersTable from './users.table';
|
||||
import roles from './roles';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const user_roles = pgTable('user_roles', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
user_id: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
||||
role_id: uuid('role_id')
|
||||
.notNull()
|
||||
.references(() => roles.id, { onDelete: 'cascade' }),
|
||||
primary: boolean('primary').default(false),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export const user_role_relations = relations(user_roles, ({ one }) => ({
|
||||
role: one(roles, {
|
||||
fields: [user_roles.role_id],
|
||||
references: [roles.id],
|
||||
}),
|
||||
user: one(usersTable, {
|
||||
fields: [user_roles.user_id],
|
||||
references: [usersTable.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type UserRoles = InferSelectModel<typeof user_roles>;
|
||||
|
||||
export default user_roles;
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import { timestamps } from '../utils';
|
||||
import user_roles from './userRoles';
|
||||
|
||||
const usersTable = pgTable('users', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
username: text('username').unique(),
|
||||
hashed_password: text('hashed_password'),
|
||||
email: text('email').unique(),
|
||||
first_name: text('first_name'),
|
||||
last_name: text('last_name'),
|
||||
verified: boolean('verified').default(false),
|
||||
receive_email: boolean('receive_email').default(false),
|
||||
theme: text('theme').default('system'),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export const userRelations = relations(usersTable, ({ many }) => ({
|
||||
user_roles: many(user_roles),
|
||||
}));
|
||||
|
||||
export type Users = InferSelectModel<typeof usersTable>;
|
||||
|
||||
export default usersTable;
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import wishlists from './wishlists';
|
||||
import games from './games';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const wishlist_items = pgTable('wishlist_items', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
wishlist_id: uuid('wishlist_id')
|
||||
.notNull()
|
||||
.references(() => wishlists.id, { onDelete: 'cascade' }),
|
||||
game_id: uuid('game_id')
|
||||
.notNull()
|
||||
.references(() => games.id, { onDelete: 'cascade' }),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export type WishlistItems = InferSelectModel<typeof wishlist_items>;
|
||||
|
||||
export const wishlist_item_relations = relations(wishlist_items, ({ one }) => ({
|
||||
wishlist: one(wishlists, {
|
||||
fields: [wishlist_items.wishlist_id],
|
||||
references: [wishlists.id],
|
||||
}),
|
||||
game: one(games, {
|
||||
fields: [wishlist_items.game_id],
|
||||
references: [games.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default wishlist_items;
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||
import usersTable from './users.table';
|
||||
import { timestamps } from '../utils';
|
||||
|
||||
const wishlists = pgTable('wishlists', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
cuid: text('cuid')
|
||||
.unique()
|
||||
.$defaultFn(() => cuid2()),
|
||||
user_id: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull().default('My Wishlist'),
|
||||
...timestamps,
|
||||
});
|
||||
|
||||
export type Wishlists = InferSelectModel<typeof wishlists>;
|
||||
|
||||
export const wishlists_relations = relations(wishlists, ({ one }) => ({
|
||||
user: one(usersTable, {
|
||||
fields: [wishlists.user_id],
|
||||
references: [usersTable.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default wishlists;
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import { Table, getTableName, sql } from 'drizzle-orm';
|
||||
import env from '../env';
|
||||
import { db, pool } from '$db';
|
||||
import * as schema from './schema';
|
||||
import * as seeds from './seeds';
|
||||
|
||||
if (!env.DB_SEEDING) {
|
||||
throw new Error('You must set DB_SEEDING to "true" when running seeds');
|
||||
}
|
||||
|
||||
async function resetTable(db: db, table: Table) {
|
||||
return db.execute(sql.raw(`TRUNCATE TABLE ${getTableName(table)} RESTART IDENTITY CASCADE`));
|
||||
}
|
||||
|
||||
for (const table of [
|
||||
schema.categories,
|
||||
schema.categoriesToExternalIds,
|
||||
schema.categories_to_games,
|
||||
schema.collection_items,
|
||||
schema.collections,
|
||||
schema.expansions,
|
||||
schema.externalIds,
|
||||
schema.games,
|
||||
schema.gamesToExternalIds,
|
||||
schema.mechanics,
|
||||
schema.mechanicsToExternalIds,
|
||||
schema.mechanics_to_games,
|
||||
schema.password_reset_tokens,
|
||||
schema.publishers,
|
||||
schema.publishersToExternalIds,
|
||||
schema.publishers_to_games,
|
||||
schema.recoveryCodes,
|
||||
schema.roles,
|
||||
schema.sessionsTable,
|
||||
schema.userRoles,
|
||||
schema.usersTable,
|
||||
schema.twoFactor,
|
||||
schema.wishlists,
|
||||
schema.wishlist_items,
|
||||
]) {
|
||||
// await db.delete(table); // clear tables without truncating / resetting ids
|
||||
await resetTable(db, table);
|
||||
}
|
||||
|
||||
await seeds.roles(db);
|
||||
await seeds.users(db);
|
||||
|
||||
await pool.end();
|
||||
process.exit();
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "admin"
|
||||
},
|
||||
{
|
||||
"name": "user"
|
||||
},
|
||||
{
|
||||
"name": "editor"
|
||||
},
|
||||
{
|
||||
"name": "moderator"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
[
|
||||
{
|
||||
"first_name": "John",
|
||||
"last_name": "Smith",
|
||||
"username": "john.smith",
|
||||
"email": "john.smith@example.com",
|
||||
"password": "password",
|
||||
"roles": [
|
||||
{
|
||||
"name": "user",
|
||||
"primary": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"first_name": "Jane",
|
||||
"last_name": "Doe",
|
||||
"username": "jane.doe",
|
||||
"email": "jane.doe@example.com",
|
||||
"password": "password",
|
||||
"roles": [
|
||||
{
|
||||
"name": "user",
|
||||
"primary": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"first_name": "Michael",
|
||||
"last_name": "Editor",
|
||||
"username": "michael.editor",
|
||||
"email": "michael.editor@example.com",
|
||||
"password": "password",
|
||||
"roles": [
|
||||
{
|
||||
"name": "editor",
|
||||
"primary": true
|
||||
},
|
||||
{
|
||||
"name": "user",
|
||||
"primary": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"first_name": "Jane",
|
||||
"last_name": "Moderator",
|
||||
"username": "jane.moderator",
|
||||
"email": "jane.moderator@example.com",
|
||||
"password": "password",
|
||||
"roles": [
|
||||
{
|
||||
"name": "moderator",
|
||||
"primary": true
|
||||
},
|
||||
{
|
||||
"name": "user",
|
||||
"primary": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export { default as users } from './users';
|
||||
export { default as roles } from './roles';
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { type db } from '$db';
|
||||
import * as schema from '$db/schema';
|
||||
import roles from './data/roles.json';
|
||||
|
||||
export default async function seed(db: db) {
|
||||
console.log('Creating roles ...');
|
||||
for (const role of roles) {
|
||||
await db.insert(schema.roles).values(role).onConflictDoNothing();
|
||||
}
|
||||
console.log('Roles created.');
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
import { eq } from 'drizzle-orm';
|
||||
import { Argon2id } from 'oslo/password';
|
||||
import { type db } from '$db';
|
||||
import * as schema from '$db/schema';
|
||||
import users from './data/users.json';
|
||||
import env from '../../env';
|
||||
|
||||
type JsonUser = {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roles: {
|
||||
name: string;
|
||||
primary: boolean;
|
||||
}[];
|
||||
};
|
||||
|
||||
type JsonRole = {
|
||||
name: string;
|
||||
primary: boolean;
|
||||
};
|
||||
|
||||
export default async function seed(db: db) {
|
||||
const adminRole = await db.select().from(schema.roles).where(eq(schema.roles.name, 'admin'));
|
||||
const userRole = await db.select().from(schema.roles).where(eq(schema.roles.name, 'user'));
|
||||
|
||||
console.log('Admin Role: ', adminRole);
|
||||
const adminUser = await db
|
||||
.insert(schema.usersTable)
|
||||
.values({
|
||||
username: `${env.ADMIN_USERNAME}`,
|
||||
email: '',
|
||||
hashed_password: await new Argon2id().hash(`${env.ADMIN_PASSWORD}`),
|
||||
first_name: 'Brad',
|
||||
last_name: 'S',
|
||||
verified: true,
|
||||
})
|
||||
.returning()
|
||||
.onConflictDoNothing();
|
||||
|
||||
console.log('Admin user created.', adminUser);
|
||||
|
||||
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.userRoles)
|
||||
.values({
|
||||
user_id: adminUser[0].id,
|
||||
role_id: adminRole[0].id,
|
||||
})
|
||||
.onConflictDoNothing();
|
||||
|
||||
console.log('Admin user given admin role.');
|
||||
|
||||
await db
|
||||
.insert(schema.userRoles)
|
||||
.values({
|
||||
user_id: adminUser[0].id,
|
||||
role_id: userRole[0].id,
|
||||
})
|
||||
.onConflictDoNothing();
|
||||
|
||||
console.log('Admin user given user role.');
|
||||
await Promise.all(
|
||||
users.map(async (user) => {
|
||||
const [insertedUser] = await db
|
||||
.insert(schema.usersTable)
|
||||
.values({
|
||||
...user,
|
||||
hashed_password: await new Argon2id().hash(user.password),
|
||||
})
|
||||
.returning();
|
||||
await db.insert(schema.collections).values({ user_id: insertedUser?.id });
|
||||
await db.insert(schema.wishlists).values({ user_id: insertedUser?.id });
|
||||
await Promise.all(
|
||||
user.roles.map(async (role: JsonRole) => {
|
||||
const foundRole = await db.query.roles.findFirst({
|
||||
where: eq(schema.roles.name, role.name),
|
||||
});
|
||||
await db.insert(schema.userRoles).values({
|
||||
user_id: insertedUser?.id,
|
||||
role_id: foundRole?.id,
|
||||
primary: role?.primary,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
// import { HTTPException } from 'hono/http-exception';
|
||||
import { timestamp } from 'drizzle-orm/pg-core';
|
||||
import { customType } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const citext = customType<{ data: string }>({
|
||||
dataType() {
|
||||
return 'citext';
|
||||
},
|
||||
});
|
||||
|
||||
export const cuid2 = customType<{ data: string }>({
|
||||
dataType() {
|
||||
return 'text';
|
||||
},
|
||||
});
|
||||
|
||||
export const takeFirst = <T>(values: T[]): T | null => {
|
||||
if (values.length === 0) return null;
|
||||
return values[0]!;
|
||||
};
|
||||
|
||||
export const takeFirstOrThrow = <T>(values: T[]): T => {
|
||||
if (values.length === 0)
|
||||
// throw new HTTPException(404, {
|
||||
// message: 'Resource not found',
|
||||
// });
|
||||
return values[0]!;
|
||||
};
|
||||
|
||||
export const timestamps = {
|
||||
createdAt: timestamp('created_at', {
|
||||
mode: 'date',
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
updatedAt: timestamp('updated_at', {
|
||||
mode: 'date',
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
};
|
||||
|
|
@ -4,7 +4,7 @@ import { hc } from 'hono/client';
|
|||
import { sequence } from '@sveltejs/kit/hooks';
|
||||
import { redirect, type Handle } from '@sveltejs/kit';
|
||||
import { dev } from '$app/environment';
|
||||
import { lucia } from '$lib/server/auth';
|
||||
// import { lucia } from '$lib/server/auth';
|
||||
import type { ApiRoutes } from '$lib/server/api';
|
||||
import { parseApiResponse } from '$lib/utils/api';
|
||||
import { StatusCodes } from '$lib/constants/status-codes';
|
||||
|
|
@ -92,7 +92,7 @@ export const authentication: Handle = async function ({ event, resolve }) {
|
|||
|
||||
export const handle: Handle = sequence(
|
||||
// Sentry.sentryHandle(),
|
||||
authentication,
|
||||
// authentication,
|
||||
apiClient
|
||||
);
|
||||
// export const handleError = Sentry.handleErrorWithSentry();
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ export const signupUsernameEmailDto = z.object({
|
|||
password: z.string({required_error: 'Password is required'}).trim(),
|
||||
confirm_password: z.string({required_error: 'Confirm Password is required'}).trim()
|
||||
})
|
||||
.superRefine(({ confirm_password, password }, ctx) => {
|
||||
refinePasswords(confirm_password, password, ctx);
|
||||
.superRefine(async ({ confirm_password, password }, ctx) => {
|
||||
return await refinePasswords(confirm_password, password, ctx);
|
||||
});
|
||||
|
||||
export type SignupUsernameEmailDto = z.infer<typeof signupUsernameEmailDto>
|
||||
|
|
|
|||
|
|
@ -1,106 +1,57 @@
|
|||
import 'reflect-metadata';
|
||||
import { Hono } from 'hono';
|
||||
import { setCookie } from 'hono/cookie';
|
||||
import {inject, injectable} from 'tsyringe';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { TimeSpan } from 'oslo';
|
||||
import type { HonoTypes } from '../types';
|
||||
import type { Controller } from '../interfaces/controller.interface';
|
||||
import { signupUsernameEmailDto } from "$lib/dtos/signup-username-email.dto";
|
||||
import {limiter} from "$lib/server/api/middleware/rate-limiter.middleware";
|
||||
import {UsersService} from "$lib/server/api/services/users.service";
|
||||
import {LoginRequestsService} from "$lib/server/api/services/loginrequest.service";
|
||||
import {LuciaProvider} from "$lib/server/api/providers";
|
||||
|
||||
@injectable()
|
||||
export class SignupController implements Controller {
|
||||
controller = new Hono<HonoTypes>();
|
||||
|
||||
constructor(
|
||||
@inject(UsersService) private readonly usersService: UsersService
|
||||
@inject(UsersService) private readonly usersService: UsersService,
|
||||
@inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService,
|
||||
@inject(LuciaProvider) private lucia: LuciaProvider
|
||||
) { }
|
||||
|
||||
routes() {
|
||||
return this.controller
|
||||
.post('/', zValidator('json', signupUsernameEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
|
||||
const { firstName, lastName, email, username, password } = await c.req.valid('json');
|
||||
const { firstName, lastName, email, username, password, confirm_password } = await c.req.valid('json');
|
||||
const existingUser = await this.usersService.findOneByUsername(username);
|
||||
|
||||
if (existingUser) {
|
||||
return c.body("User already exists", 400);
|
||||
}
|
||||
|
||||
const user = await this.usersService.create(signupUsernameEmailDto);
|
||||
const user = await this.usersService.create({ firstName, lastName, email, username, password, confirm_password });
|
||||
|
||||
// const existing_user = await db.query.usersTable.findFirst({
|
||||
// where: eq(usersTable.username, form.data.username),
|
||||
// });
|
||||
//
|
||||
// if (existing_user) {
|
||||
// return setError(form, 'username', 'You cannot create an account with that username');
|
||||
// }
|
||||
//
|
||||
// console.log('Creating user');
|
||||
//
|
||||
// const hashedPassword = await new Argon2id().hash(form.data.password);
|
||||
//
|
||||
// const user = await db
|
||||
// .insert(usersTable)
|
||||
// .values({
|
||||
// username: form.data.username,
|
||||
// hashed_password: hashedPassword,
|
||||
// email: form.data.email,
|
||||
// first_name: form.data.firstName ?? '',
|
||||
// last_name: form.data.lastName ?? '',
|
||||
// verified: false,
|
||||
// receive_email: false,
|
||||
// theme: 'system',
|
||||
// })
|
||||
// .returning();
|
||||
// console.log('signup user', user);
|
||||
//
|
||||
// if (!user || user.length === 0) {
|
||||
// return fail(400, {
|
||||
// form,
|
||||
// message: `Could not create your account. Please try again. If the problem persists, please contact support. Error ID: ${cuid2()}`,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// await add_user_to_role(user[0].id, 'user', true);
|
||||
// await db.insert(collections).values({
|
||||
// user_id: user[0].id,
|
||||
// });
|
||||
// await db.insert(wishlists).values({
|
||||
// user_id: user[0].id,
|
||||
// });
|
||||
//
|
||||
// try {
|
||||
// session = await lucia.createSession(user[0].id, {
|
||||
// ip_country: event.locals.ip,
|
||||
// ip_address: event.locals.country,
|
||||
// twoFactorAuthEnabled: false,
|
||||
// isTwoFactorAuthenticated: false,
|
||||
// });
|
||||
// sessionCookie = lucia.createSessionCookie(session.id);
|
||||
// } catch (e: any) {
|
||||
// if (e.message.toUpperCase() === `DUPLICATE_KEY_ID`) {
|
||||
// // key already exists
|
||||
// console.error('Lucia Error: ', e);
|
||||
// }
|
||||
// console.log(e);
|
||||
// const message = {
|
||||
// type: 'error',
|
||||
// message: 'Unable to create your account. Please try again.',
|
||||
// };
|
||||
// form.data.password = '';
|
||||
// form.data.confirm_password = '';
|
||||
// error(500, message);
|
||||
// }
|
||||
//
|
||||
// event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
// path: '.',
|
||||
// ...sessionCookie.attributes,
|
||||
// });
|
||||
//
|
||||
// redirect(302, '/');
|
||||
if (!user) {
|
||||
return c.body("Failed to create user", 500);
|
||||
}
|
||||
|
||||
return c.json({ message: 'Hello, world!' });
|
||||
const session = await this.loginRequestService.createUserSession(user.id, c.req, undefined);
|
||||
const sessionCookie = this.lucia.createSessionCookie(session.id);
|
||||
console.log("set cookie", sessionCookie);
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge: sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge : new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires
|
||||
});
|
||||
return c.json({ message: 'ok' });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// lib/server/lucia.ts
|
||||
import { Lucia, TimeSpan } from 'lucia';
|
||||
import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';
|
||||
import db from '$db/index';
|
||||
import { sessionsTable, usersTable } from '$db/schema';
|
||||
import { db } from '../database';
|
||||
import { sessionsTable, usersTable } from '../database/tables';
|
||||
import { config } from '../../common/config';
|
||||
|
||||
const adapter = new DrizzlePostgreSQLAdapter(db, sessionsTable, usersTable);
|
||||
|
|
@ -18,7 +18,12 @@ export const lucia = new Lucia(adapter, {
|
|||
},
|
||||
getUserAttributes: (attributes) => {
|
||||
return {
|
||||
...attributes,
|
||||
// ...attributes,
|
||||
username: attributes.username,
|
||||
email: attributes.email,
|
||||
firstName: attributes.firstName,
|
||||
lastName: attributes.lastName,
|
||||
theme: attributes.theme,
|
||||
};
|
||||
},
|
||||
sessionExpiresIn: new TimeSpan(2, 'w'), // 2 weeks
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { pgTable, text, uuid } from "drizzle-orm/pg-core";
|
||||
import { type InferSelectModel } from 'drizzle-orm';
|
||||
import { timestamps } from '../utils';
|
||||
import { usersTable } from "./users.table";
|
||||
|
||||
|
|
@ -18,3 +19,5 @@ export const credentialsTable = pgTable('credentials', {
|
|||
secret_data: text('secret_data').notNull(),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
export type Credentials = InferSelectModel<typeof credentialsTable>;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ export const requireAuth: MiddlewareHandler<{
|
|||
};
|
||||
}> = createMiddleware(async (c, next) => {
|
||||
const user = c.var.user;
|
||||
const session = c.var.session;
|
||||
if (!user) throw Unauthorized('You must be logged in to access this resource');
|
||||
return next();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { UsersRepository } from '../repositories/users.repository';
|
|||
import { CredentialsRepository } from '../repositories/credentials.repository';
|
||||
import type { HonoRequest } from 'hono';
|
||||
import type {SigninUsernameDto} from "$lib/dtos/signin-username.dto";
|
||||
import type {Credentials} from "$lib/server/api/infrastructure/database/tables";
|
||||
|
||||
@injectable()
|
||||
export class LoginRequestsService {
|
||||
|
|
@ -53,17 +54,23 @@ export class LoginRequestsService {
|
|||
|
||||
const totpCredentials = await this.credentialsRepository.findTOTPCredentialsByUserId(existingUser.id);
|
||||
|
||||
return this.lucia.createSession(existingUser.id, {
|
||||
ip_country: requestIpCountry || 'unknown',
|
||||
ip_address: requestIpAddress || 'unknown',
|
||||
twoFactorAuthEnabled:
|
||||
!!totpCredentials &&
|
||||
totpCredentials?.secret_data !== null &&
|
||||
totpCredentials?.secret_data !== '',
|
||||
isTwoFactorAuthenticated: false,
|
||||
});
|
||||
return await this.createUserSession(existingUser.id, req, totpCredentials);
|
||||
}
|
||||
|
||||
async createUserSession(existingUserId: string, req: HonoRequest, totpCredentials: Credentials | undefined) {
|
||||
const requestIpAddress = req.header('x-real-ip');
|
||||
const requestIpCountry = req.header('x-vercel-ip-country');
|
||||
return this.lucia.createSession(existingUserId, {
|
||||
ip_country: requestIpCountry || 'unknown',
|
||||
ip_address: requestIpAddress || 'unknown',
|
||||
twoFactorAuthEnabled:
|
||||
!!totpCredentials &&
|
||||
totpCredentials?.secret_data !== null &&
|
||||
totpCredentials?.secret_data !== '',
|
||||
isTwoFactorAuthenticated: false,
|
||||
});
|
||||
}
|
||||
|
||||
// Create a new user and send a welcome email - or other onboarding process
|
||||
private async handleNewUserRegistration(email: string) {
|
||||
const newUser = await this.usersRepository.create({ email, verified: true })
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { generateIdFromEntropySize, type Session, type User } from 'lucia';
|
||||
import { TimeSpan, createDate } from 'oslo';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import db from '../../db';
|
||||
import { password_reset_tokens } from '$db/schema';
|
||||
import { db } from './api/infrastructure/database/index';
|
||||
import { password_reset_tokens } from './api/infrastructure/database/tables';
|
||||
|
||||
export async function createPasswordResetToken(userId: string): Promise<string> {
|
||||
// optionally invalidate all existing tokens
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
// lib/server/lucia.ts
|
||||
import { Lucia, TimeSpan } from 'lucia';
|
||||
import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';
|
||||
import db from '../../db';
|
||||
import { sessionsTable, usersTable } from '$db/schema';
|
||||
|
||||
const adapter = new DrizzlePostgreSQLAdapter(db, sessionsTable, usersTable);
|
||||
|
||||
let domain;
|
||||
if (process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production') {
|
||||
domain = 'boredgame.vercel.app';
|
||||
} else if (process.env.VERCEL_ENV === 'preview' || process.env.VERCEL_ENV === 'development') {
|
||||
domain = process.env.VERCEL_BRANCH_URL;
|
||||
} else {
|
||||
domain = 'localhost';
|
||||
}
|
||||
|
||||
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 {
|
||||
username: attributes.username,
|
||||
email: attributes.email,
|
||||
firstName: attributes.firstName,
|
||||
lastName: attributes.lastName,
|
||||
theme: attributes.theme,
|
||||
};
|
||||
},
|
||||
sessionExpiresIn: new TimeSpan(30, 'd'), // 30 days
|
||||
sessionCookie: {
|
||||
name: 'session',
|
||||
expires: false, // session cookies have very long lifespan (2 years)
|
||||
attributes: {
|
||||
// set to `true` when using HTTPS
|
||||
secure: process.env.NODE_ENV === 'production' || process.env.VERCEL_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
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;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
theme: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@ export const signUpSchema = userSchema
|
|||
password: true,
|
||||
confirm_password: true,
|
||||
})
|
||||
.superRefine(({ confirm_password, password }, ctx) => {
|
||||
refinePasswords(confirm_password, password, ctx);
|
||||
.superRefine(async ({ confirm_password, password }, ctx) => {
|
||||
return await refinePasswords(confirm_password, password, ctx);
|
||||
});
|
||||
|
||||
export const signInSchema = z.object({
|
||||
|
|
|
|||
|
|
@ -60,18 +60,18 @@ export const actions: Actions = {
|
|||
where: eq(usersTable.id, user!.id),
|
||||
});
|
||||
|
||||
if (!dbUser?.hashed_password) {
|
||||
form.data.password = '';
|
||||
form.data.confirm_password = '';
|
||||
form.data.current_password = '';
|
||||
return setError(
|
||||
form,
|
||||
'Error occurred. Please try again or contact support if you need further help.',
|
||||
);
|
||||
}
|
||||
// if (!dbUser?.hashed_password) {
|
||||
// form.data.password = '';
|
||||
// form.data.confirm_password = '';
|
||||
// form.data.current_password = '';
|
||||
// return setError(
|
||||
// form,
|
||||
// 'Error occurred. Please try again or contact support if you need further help.',
|
||||
// );
|
||||
// }
|
||||
|
||||
const currentPasswordVerified = await new Argon2id().verify(
|
||||
dbUser.hashed_password,
|
||||
// dbUser.hashed_password,
|
||||
form.data.current_password,
|
||||
);
|
||||
|
||||
|
|
@ -86,10 +86,10 @@ export const actions: Actions = {
|
|||
}
|
||||
const hashedPassword = await new Argon2id().hash(form.data.password);
|
||||
await lucia.invalidateUserSessions(user.id);
|
||||
await db
|
||||
.update(usersTable)
|
||||
.set({ hashed_password: hashedPassword })
|
||||
.where(eq(usersTable.id, user.id));
|
||||
// await db
|
||||
// .update(usersTable)
|
||||
// .set({ hashed_password: hashedPassword })
|
||||
// .where(eq(usersTable.id, user.id));
|
||||
await lucia.createSession(user.id, {
|
||||
country: event.locals.session?.ipCountry ?? 'unknown',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -115,14 +115,14 @@ export const actions: Actions = {
|
|||
where: eq(usersTable.id, user!.id!),
|
||||
});
|
||||
|
||||
if (!dbUser?.hashed_password) {
|
||||
addTwoFactorForm.data.current_password = '';
|
||||
addTwoFactorForm.data.two_factor_code = '';
|
||||
return setError(
|
||||
addTwoFactorForm,
|
||||
'Error occurred. Please try again or contact support if you need further help.',
|
||||
);
|
||||
}
|
||||
// if (!dbUser?.hashed_password) {
|
||||
// addTwoFactorForm.data.current_password = '';
|
||||
// addTwoFactorForm.data.two_factor_code = '';
|
||||
// return setError(
|
||||
// addTwoFactorForm,
|
||||
// 'Error occurred. Please try again or contact support if you need further help.',
|
||||
// );
|
||||
// }
|
||||
|
||||
const twoFactorDetails = await db.query.twoFactor.findFirst({
|
||||
where: eq(twoFactor.userId, dbUser?.id),
|
||||
|
|
@ -147,7 +147,7 @@ export const actions: Actions = {
|
|||
}
|
||||
|
||||
const currentPasswordVerified = await new Argon2id().verify(
|
||||
dbUser.hashed_password,
|
||||
// dbUser.hashed_password,
|
||||
addTwoFactorForm.data.current_password,
|
||||
);
|
||||
|
||||
|
|
@ -194,16 +194,16 @@ export const actions: Actions = {
|
|||
where: eq(usersTable.id, user.id),
|
||||
});
|
||||
|
||||
if (!dbUser?.hashed_password) {
|
||||
removeTwoFactorForm.data.current_password = '';
|
||||
return setError(
|
||||
removeTwoFactorForm,
|
||||
'Error occurred. Please try again or contact support if you need further help.',
|
||||
);
|
||||
}
|
||||
// if (!dbUser?.hashed_password) {
|
||||
// removeTwoFactorForm.data.current_password = '';
|
||||
// return setError(
|
||||
// removeTwoFactorForm,
|
||||
// 'Error occurred. Please try again or contact support if you need further help.',
|
||||
// );
|
||||
// }
|
||||
|
||||
const currentPasswordVerified = await new Argon2id().verify(
|
||||
dbUser.hashed_password,
|
||||
// dbUser.hashed_password,
|
||||
removeTwoFactorForm.data.current_password,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
import { loadFlash } from 'sveltekit-flash-message/server';
|
||||
import type { LayoutServerLoad } from '../$types';
|
||||
import { userFullyAuthenticated, userNotFullyAuthenticated } from '$lib/server/auth-utils';
|
||||
import { lucia } from '$lib/server/auth';
|
||||
// import { userFullyAuthenticated, userNotFullyAuthenticated } from '$lib/server/auth-utils';
|
||||
// import { lucia } from '$lib/server/auth';
|
||||
|
||||
export const load: LayoutServerLoad = loadFlash(async (event) => {
|
||||
const { url, locals, cookies } = event;
|
||||
const { user, session } = locals;
|
||||
const authedUser = await locals.getAuthedUserOrThrow();
|
||||
|
||||
if (userNotFullyAuthenticated(user, session)) {
|
||||
await lucia.invalidateSession(locals.session!.id!);
|
||||
const sessionCookie = lucia.createBlankSessionCookie();
|
||||
cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
path: '.',
|
||||
...sessionCookie.attributes,
|
||||
});
|
||||
}
|
||||
// if (userNotFullyAuthenticated(user, session)) {
|
||||
// await lucia.invalidateSession(locals.session!.id!);
|
||||
// const sessionCookie = lucia.createBlankSessionCookie();
|
||||
// cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
// path: '.',
|
||||
// ...sessionCookie.attributes,
|
||||
// });
|
||||
// }
|
||||
|
||||
return {
|
||||
url: url.pathname,
|
||||
user: userFullyAuthenticated(user, session) ? locals.user : null,
|
||||
// user: userFullyAuthenticated(user, session) ? locals.user : null,
|
||||
user: authedUser,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import type { MetaTagsProps } from 'svelte-meta-tags';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import db from '../../db';
|
||||
import { collections, usersTable, wishlists } from '$db/schema';
|
||||
import { userFullyAuthenticated } from '$lib/server/auth-utils';
|
||||
import {db} from '$lib/server/api/infrastructure/database/index';
|
||||
import { collections, usersTable, wishlists } from '$lib/server/api/infrastructure/database/tables';
|
||||
// import { userFullyAuthenticated } from '$lib/server/auth-utils';
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
const { locals, url } = event;
|
||||
const { user, session } = locals;
|
||||
|
||||
const authedUser = await locals.getAuthedUser();
|
||||
|
||||
const image = {
|
||||
url: `${
|
||||
|
|
@ -41,9 +42,9 @@ export const load: PageServerLoad = async (event) => {
|
|||
},
|
||||
});
|
||||
|
||||
if (userFullyAuthenticated(user, session)) {
|
||||
// if (userFullyAuthenticated(user, session)) {
|
||||
const dbUser = await db.query.usersTable.findFirst({
|
||||
where: eq(usersTable.id, user!.id!),
|
||||
where: eq(usersTable.id, authedUser!.id!),
|
||||
});
|
||||
|
||||
console.log('Sending back user details');
|
||||
|
|
@ -52,14 +53,14 @@ export const load: PageServerLoad = async (event) => {
|
|||
cuid: true,
|
||||
name: true,
|
||||
},
|
||||
where: eq(wishlists.user_id, user!.id!),
|
||||
where: eq(wishlists.user_id, authedUser!.id!),
|
||||
});
|
||||
const userCollection = await db.query.collections.findMany({
|
||||
columns: {
|
||||
cuid: true,
|
||||
name: true,
|
||||
},
|
||||
where: eq(collections.user_id, user!.id!),
|
||||
where: eq(collections.user_id, authedUser!.id!),
|
||||
});
|
||||
|
||||
console.log('Wishlists', userWishlists);
|
||||
|
|
@ -74,7 +75,7 @@ export const load: PageServerLoad = async (event) => {
|
|||
wishlists: userWishlists,
|
||||
collections: userCollection,
|
||||
};
|
||||
}
|
||||
// }
|
||||
|
||||
return { metaTagsChild: metaTags, user: null, wishlists: [], collections: [] };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import { Argon2id } from 'oslo/password';
|
|||
import { zod } from 'sveltekit-superforms/adapters';
|
||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||
import { redirect } from 'sveltekit-flash-message/server';
|
||||
import { RateLimiter } from 'sveltekit-rate-limiter/server';
|
||||
import db from '../../../db';
|
||||
import { lucia } from '$lib/server/auth';
|
||||
import { twoFactor, usersTable, type Users } from '$db/schema';
|
||||
import { db } from '../../../lib/server/api/infrastructure/database/index';
|
||||
import { lucia } from '../../../lib/server/api/infrastructure/auth/lucia';
|
||||
import { credentialsTable, usersTable } from '../../../lib/server/api/infrastructure/database/tables';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import {signinUsernameDto} from "$lib/dtos/signin-username.dto";
|
||||
|
||||
|
|
@ -41,10 +40,6 @@ export const load: PageServerLoad = async (event) => {
|
|||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
// if (await limiter.isLimited(event)) {
|
||||
// throw error(429);
|
||||
// }
|
||||
|
||||
const { locals } = event;
|
||||
|
||||
const authedUser = await locals.getAuthedUser();
|
||||
|
|
@ -66,68 +61,68 @@ export const actions: Actions = {
|
|||
});
|
||||
}
|
||||
|
||||
let session;
|
||||
let sessionCookie;
|
||||
const user: Users | undefined = await db.query.usersTable.findFirst({
|
||||
where: or(eq(usersTable.username, form.data.username), eq(usersTable.email, form.data.username)),
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
form.data.password = '';
|
||||
return setError(form, 'username', 'Your username or password is incorrect.');
|
||||
}
|
||||
|
||||
let twoFactorDetails;
|
||||
|
||||
// let session;
|
||||
// let sessionCookie;
|
||||
// const user: Users | undefined = await db.query.usersTable.findFirst({
|
||||
// where: or(eq(usersTable.username, form.data.username), eq(usersTable.email, form.data.username)),
|
||||
// });
|
||||
//
|
||||
// if (!user) {
|
||||
// form.data.password = '';
|
||||
// return setError(form, 'username', 'Your username or password is incorrect.');
|
||||
// }
|
||||
//
|
||||
// let twoFactorDetails;
|
||||
//
|
||||
try {
|
||||
const password = form.data.password;
|
||||
console.log('user', JSON.stringify(user, null, 2));
|
||||
|
||||
if (!user?.hashed_password) {
|
||||
console.log('invalid username/password');
|
||||
form.data.password = '';
|
||||
return setError(form, 'password', 'Your username or password is incorrect.');
|
||||
}
|
||||
|
||||
const validPassword = await new Argon2id().verify(user.hashed_password, password);
|
||||
if (!validPassword) {
|
||||
console.log('invalid password');
|
||||
form.data.password = '';
|
||||
return setError(form, 'password', 'Your username or password is incorrect.');
|
||||
}
|
||||
|
||||
console.log('ip', locals.ip);
|
||||
console.log('country', locals.country);
|
||||
|
||||
twoFactorDetails = await db.query.twoFactor.findFirst({
|
||||
where: eq(twoFactor.userId, user?.id),
|
||||
});
|
||||
|
||||
if (twoFactorDetails?.secret && twoFactorDetails?.enabled) {
|
||||
await db.update(twoFactor).set({
|
||||
initiatedTime: new Date(),
|
||||
});
|
||||
|
||||
session = await lucia.createSession(user.id, {
|
||||
ip_country: locals.country,
|
||||
ip_address: locals.ip,
|
||||
twoFactorAuthEnabled:
|
||||
twoFactorDetails?.enabled &&
|
||||
twoFactorDetails?.secret !== null &&
|
||||
twoFactorDetails?.secret !== '',
|
||||
isTwoFactorAuthenticated: false,
|
||||
});
|
||||
} else {
|
||||
session = await lucia.createSession(user.id, {
|
||||
ip_country: locals.country,
|
||||
ip_address: locals.ip,
|
||||
twoFactorAuthEnabled: false,
|
||||
isTwoFactorAuthenticated: false,
|
||||
});
|
||||
}
|
||||
console.log('logging in session', session);
|
||||
sessionCookie = lucia.createSessionCookie(session.id);
|
||||
console.log('logging in session cookie', sessionCookie);
|
||||
// const password = form.data.password;
|
||||
// console.log('user', JSON.stringify(user, null, 2));
|
||||
//
|
||||
// if (!user?.hashed_password) {
|
||||
// console.log('invalid username/password');
|
||||
// form.data.password = '';
|
||||
// return setError(form, 'password', 'Your username or password is incorrect.');
|
||||
// }
|
||||
//
|
||||
// const validPassword = await new Argon2id().verify(user.hashed_password, password);
|
||||
// if (!validPassword) {
|
||||
// console.log('invalid password');
|
||||
// form.data.password = '';
|
||||
// return setError(form, 'password', 'Your username or password is incorrect.');
|
||||
// }
|
||||
//
|
||||
// console.log('ip', locals.ip);
|
||||
// console.log('country', locals.country);
|
||||
//
|
||||
// twoFactorDetails = await db.query.twoFactor.findFirst({
|
||||
// where: eq(twoFactor.userId, user?.id),
|
||||
// });
|
||||
//
|
||||
// if (twoFactorDetails?.secret && twoFactorDetails?.enabled) {
|
||||
// await db.update(twoFactor).set({
|
||||
// initiatedTime: new Date(),
|
||||
// });
|
||||
//
|
||||
// session = await lucia.createSession(user.id, {
|
||||
// ip_country: locals.country,
|
||||
// ip_address: locals.ip,
|
||||
// twoFactorAuthEnabled:
|
||||
// twoFactorDetails?.enabled &&
|
||||
// twoFactorDetails?.secret !== null &&
|
||||
// twoFactorDetails?.secret !== '',
|
||||
// isTwoFactorAuthenticated: false,
|
||||
// });
|
||||
// } else {
|
||||
// session = await lucia.createSession(user.id, {
|
||||
// ip_country: locals.country,
|
||||
// ip_address: locals.ip,
|
||||
// twoFactorAuthEnabled: false,
|
||||
// isTwoFactorAuthenticated: false,
|
||||
// });
|
||||
// }
|
||||
// console.log('logging in session', session);
|
||||
// sessionCookie = lucia.createSessionCookie(session.id);
|
||||
// console.log('logging in session cookie', sessionCookie);
|
||||
} catch (e) {
|
||||
// TODO: need to return error message to the client
|
||||
console.error(e);
|
||||
|
|
@ -135,26 +130,26 @@ export const actions: Actions = {
|
|||
return setError(form, '', 'Your username or password is incorrect.');
|
||||
}
|
||||
|
||||
console.log('setting session cookie', sessionCookie);
|
||||
event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
path: '.',
|
||||
...sessionCookie.attributes,
|
||||
});
|
||||
// console.log('setting session cookie', sessionCookie);
|
||||
// event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
// path: '.',
|
||||
// ...sessionCookie.attributes,
|
||||
// });
|
||||
|
||||
form.data.username = '';
|
||||
form.data.password = '';
|
||||
|
||||
if (
|
||||
twoFactorDetails?.enabled &&
|
||||
twoFactorDetails?.secret !== null &&
|
||||
twoFactorDetails?.secret !== ''
|
||||
) {
|
||||
console.log('redirecting to TOTP page');
|
||||
const message = { type: 'success', message: 'Please enter your TOTP code.' } as const;
|
||||
redirect(302, '/totp', message, event);
|
||||
} else {
|
||||
const message = { type: 'success', message: 'Signed In!' } as const;
|
||||
redirect(302, '/', message, event);
|
||||
}
|
||||
// if (
|
||||
// twoFactorDetails?.enabled &&
|
||||
// twoFactorDetails?.secret !== null &&
|
||||
// twoFactorDetails?.secret !== ''
|
||||
// ) {
|
||||
// console.log('redirecting to TOTP page');
|
||||
// const message = { type: 'success', message: 'Please enter your TOTP code.' } as const;
|
||||
// redirect(302, '/totp', message, event);
|
||||
// } else {
|
||||
// const message = { type: 'success', message: 'Signed In!' } as const;
|
||||
// redirect(302, '/', message, event);
|
||||
// }
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { add_user_to_role } from '$server/roles';
|
|||
import db from '../../../db';
|
||||
import { collections, usersTable, wishlists } from '$db/schema';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import {signupUsernameEmailDto} from "$lib/dtos/signup-username-email.dto";
|
||||
|
||||
const limiter = new RateLimiter({
|
||||
// A rate is defined by [number, unit]
|
||||
|
|
@ -55,7 +56,7 @@ export const load: PageServerLoad = async (event) => {
|
|||
// }
|
||||
|
||||
return {
|
||||
form: await superValidate(zod(signUpSchema), {
|
||||
form: await superValidate(zod(signupUsernameEmailDto), {
|
||||
defaults: signUpDefaults,
|
||||
}),
|
||||
};
|
||||
|
|
@ -63,11 +64,20 @@ export const load: PageServerLoad = async (event) => {
|
|||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
if (await limiter.isLimited(event)) {
|
||||
throw error(429);
|
||||
const { locals } = event;
|
||||
|
||||
const authedUser = await locals.getAuthedUser();
|
||||
|
||||
if (authedUser) {
|
||||
const message = { type: 'success', message: 'You are already signed in' } as const;
|
||||
throw redirect('/', message, event);
|
||||
}
|
||||
// fail(401, { message: 'Sign-up not yet available. Please add your email to the waitlist!' });
|
||||
const form = await superValidate(event, zod(signUpSchema));
|
||||
|
||||
const form = await superValidate(event, zod(signupUsernameEmailDto));
|
||||
|
||||
const { error } = await locals.api.signup.$post({ json: form.data }).then(locals.parseApiResponse);
|
||||
if (error) return setError(form, 'username', error);
|
||||
|
||||
if (!form.valid) {
|
||||
form.data.password = '';
|
||||
form.data.confirm_password = '';
|
||||
|
|
@ -76,80 +86,80 @@ export const actions: Actions = {
|
|||
});
|
||||
}
|
||||
|
||||
let session;
|
||||
let sessionCookie;
|
||||
// Adding user to the db
|
||||
console.log('Check if user already exists');
|
||||
|
||||
const existing_user = await db.query.usersTable.findFirst({
|
||||
where: eq(usersTable.username, form.data.username),
|
||||
});
|
||||
|
||||
if (existing_user) {
|
||||
return setError(form, 'username', 'You cannot create an account with that username');
|
||||
}
|
||||
|
||||
console.log('Creating user');
|
||||
|
||||
const hashedPassword = await new Argon2id().hash(form.data.password);
|
||||
|
||||
const user = await db
|
||||
.insert(usersTable)
|
||||
.values({
|
||||
username: form.data.username,
|
||||
hashed_password: hashedPassword,
|
||||
email: form.data.email,
|
||||
first_name: form.data.firstName ?? '',
|
||||
last_name: form.data.lastName ?? '',
|
||||
verified: false,
|
||||
receive_email: false,
|
||||
theme: 'system',
|
||||
})
|
||||
.returning();
|
||||
console.log('signup user', user);
|
||||
|
||||
if (!user || user.length === 0) {
|
||||
return fail(400, {
|
||||
form,
|
||||
message: `Could not create your account. Please try again. If the problem persists, please contact support. Error ID: ${cuid2()}`,
|
||||
});
|
||||
}
|
||||
|
||||
await add_user_to_role(user[0].id, 'user', true);
|
||||
await db.insert(collections).values({
|
||||
user_id: user[0].id,
|
||||
});
|
||||
await db.insert(wishlists).values({
|
||||
user_id: user[0].id,
|
||||
});
|
||||
|
||||
try {
|
||||
session = await lucia.createSession(user[0].id, {
|
||||
ip_country: event.locals.ip,
|
||||
ip_address: event.locals.country,
|
||||
twoFactorAuthEnabled: false,
|
||||
isTwoFactorAuthenticated: false,
|
||||
});
|
||||
sessionCookie = lucia.createSessionCookie(session.id);
|
||||
} catch (e: any) {
|
||||
if (e.message.toUpperCase() === `DUPLICATE_KEY_ID`) {
|
||||
// key already exists
|
||||
console.error('Lucia Error: ', e);
|
||||
}
|
||||
console.log(e);
|
||||
const message = {
|
||||
type: 'error',
|
||||
message: 'Unable to create your account. Please try again.',
|
||||
};
|
||||
form.data.password = '';
|
||||
form.data.confirm_password = '';
|
||||
error(500, message);
|
||||
}
|
||||
|
||||
event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
path: '.',
|
||||
...sessionCookie.attributes,
|
||||
});
|
||||
// let session;
|
||||
// let sessionCookie;
|
||||
// // Adding user to the db
|
||||
// console.log('Check if user already exists');
|
||||
//
|
||||
// const existing_user = await db.query.usersTable.findFirst({
|
||||
// where: eq(usersTable.username, form.data.username),
|
||||
// });
|
||||
//
|
||||
// if (existing_user) {
|
||||
// return setError(form, 'username', 'You cannot create an account with that username');
|
||||
// }
|
||||
//
|
||||
// console.log('Creating user');
|
||||
//
|
||||
// const hashedPassword = await new Argon2id().hash(form.data.password);
|
||||
//
|
||||
// const user = await db
|
||||
// .insert(usersTable)
|
||||
// .values({
|
||||
// username: form.data.username,
|
||||
// hashed_password: hashedPassword,
|
||||
// email: form.data.email,
|
||||
// first_name: form.data.firstName ?? '',
|
||||
// last_name: form.data.lastName ?? '',
|
||||
// verified: false,
|
||||
// receive_email: false,
|
||||
// theme: 'system',
|
||||
// })
|
||||
// .returning();
|
||||
// console.log('signup user', user);
|
||||
//
|
||||
// if (!user || user.length === 0) {
|
||||
// return fail(400, {
|
||||
// form,
|
||||
// message: `Could not create your account. Please try again. If the problem persists, please contact support. Error ID: ${cuid2()}`,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// await add_user_to_role(user[0].id, 'user', true);
|
||||
// await db.insert(collections).values({
|
||||
// user_id: user[0].id,
|
||||
// });
|
||||
// await db.insert(wishlists).values({
|
||||
// user_id: user[0].id,
|
||||
// });
|
||||
//
|
||||
// try {
|
||||
// session = await lucia.createSession(user[0].id, {
|
||||
// ip_country: event.locals.ip,
|
||||
// ip_address: event.locals.country,
|
||||
// twoFactorAuthEnabled: false,
|
||||
// isTwoFactorAuthenticated: false,
|
||||
// });
|
||||
// sessionCookie = lucia.createSessionCookie(session.id);
|
||||
// } catch (e: any) {
|
||||
// if (e.message.toUpperCase() === `DUPLICATE_KEY_ID`) {
|
||||
// // key already exists
|
||||
// console.error('Lucia Error: ', e);
|
||||
// }
|
||||
// console.log(e);
|
||||
// const message = {
|
||||
// type: 'error',
|
||||
// message: 'Unable to create your account. Please try again.',
|
||||
// };
|
||||
// form.data.password = '';
|
||||
// form.data.confirm_password = '';
|
||||
// error(500, message);
|
||||
// }
|
||||
//
|
||||
// event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
// path: '.',
|
||||
// ...sessionCookie.attributes,
|
||||
// });
|
||||
|
||||
redirect(302, '/');
|
||||
// const message = { type: 'success', message: 'Signed Up!' } as const;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { loadFlash } from 'sveltekit-flash-message/server';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
|
||||
export const load: LayoutServerLoad = loadFlash(async ({ url, locals }) => {
|
||||
export const load: LayoutServerLoad = loadFlash(async (event) => {
|
||||
const { locals, url } = event;
|
||||
const user = await locals.getAuthedUser();
|
||||
return {
|
||||
url: url.pathname,
|
||||
user: locals.user
|
||||
user,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export async function POST({ request, params }) {
|
|||
|
||||
await lucia.invalidateUserSessions(token.user_id);
|
||||
const hashPassword = await new Argon2id().hash(password);
|
||||
await db.update(usersTable).set({ hashed_password: hashPassword }).where(eq(usersTable.id, token.user_id));
|
||||
// await db.update(usersTable).set({ hashed_password: hashPassword }).where(eq(usersTable.id, token.user_id));
|
||||
|
||||
const session = await lucia.createSession(token.user_id, {});
|
||||
const sessionCookie = lucia.createSessionCookie(session.id);
|
||||
|
|
|
|||
Loading…
Reference in a new issue