Change prisma to drizzle in password change, profile update, collection page, and wishlist page.

This commit is contained in:
Bradley Shellnut 2024-02-18 00:03:08 -08:00
parent 9a87af4c3c
commit bbb6df0fec
14 changed files with 1318 additions and 128 deletions

View file

@ -0,0 +1 @@
ALTER TABLE "collection_items" ADD COLUMN "times_played" integer DEFAULT 0;

File diff suppressed because it is too large Load diff

View file

@ -71,6 +71,13 @@
"when": 1708105890146, "when": 1708105890146,
"tag": "0009_equal_christian_walker", "tag": "0009_equal_christian_walker",
"breakpoints": true "breakpoints": true
},
{
"idx": 10,
"version": "5",
"when": 1708243232524,
"tag": "0010_flat_mister_sinister",
"breakpoints": true
} }
] ]
} }

View file

@ -26,7 +26,7 @@
}, },
"devDependencies": { "devDependencies": {
"@melt-ui/pp": "^0.3.0", "@melt-ui/pp": "^0.3.0",
"@melt-ui/svelte": "^0.74.0", "@melt-ui/svelte": "^0.74.1",
"@playwright/test": "^1.41.2", "@playwright/test": "^1.41.2",
"@resvg/resvg-js": "^2.6.0", "@resvg/resvg-js": "^2.6.0",
"@sveltejs/adapter-auto": "^3.1.1", "@sveltejs/adapter-auto": "^3.1.1",
@ -110,7 +110,7 @@
"mysql2": "^3.9.1", "mysql2": "^3.9.1",
"nanoid": "^5.0.5", "nanoid": "^5.0.5",
"open-props": "^1.6.18", "open-props": "^1.6.18",
"oslo": "^1.1.1", "oslo": "^1.1.2",
"pg": "^8.11.3", "pg": "^8.11.3",
"postgres": "^3.4.3", "postgres": "^3.4.3",
"radix-svelte": "^0.9.0", "radix-svelte": "^0.9.0",

View file

@ -99,8 +99,8 @@ dependencies:
specifier: ^1.6.18 specifier: ^1.6.18
version: 1.6.18 version: 1.6.18
oslo: oslo:
specifier: ^1.1.1 specifier: ^1.1.2
version: 1.1.1 version: 1.1.2
pg: pg:
specifier: ^8.11.3 specifier: ^8.11.3
version: 8.11.3 version: 8.11.3
@ -132,10 +132,10 @@ dependencies:
devDependencies: devDependencies:
'@melt-ui/pp': '@melt-ui/pp':
specifier: ^0.3.0 specifier: ^0.3.0
version: 0.3.0(@melt-ui/svelte@0.74.0)(svelte@4.2.11) version: 0.3.0(@melt-ui/svelte@0.74.1)(svelte@4.2.11)
'@melt-ui/svelte': '@melt-ui/svelte':
specifier: ^0.74.0 specifier: ^0.74.1
version: 0.74.0(svelte@4.2.11) version: 0.74.1(svelte@4.2.11)
'@playwright/test': '@playwright/test':
specifier: ^1.41.2 specifier: ^1.41.2
version: 1.41.2 version: 1.41.2
@ -1745,14 +1745,14 @@ packages:
- supports-color - supports-color
dev: false dev: false
/@melt-ui/pp@0.3.0(@melt-ui/svelte@0.74.0)(svelte@4.2.11): /@melt-ui/pp@0.3.0(@melt-ui/svelte@0.74.1)(svelte@4.2.11):
resolution: {integrity: sha512-b07Bdh8l2KcwKVCXOY+SoBw1dk9eWvQfMSi6SoacpRVyVmmfpi0kV4oGt3HYF0tUCB3sEmVicxse50ZzZxEzEA==} resolution: {integrity: sha512-b07Bdh8l2KcwKVCXOY+SoBw1dk9eWvQfMSi6SoacpRVyVmmfpi0kV4oGt3HYF0tUCB3sEmVicxse50ZzZxEzEA==}
engines: {pnpm: '>=8.6.3'} engines: {pnpm: '>=8.6.3'}
peerDependencies: peerDependencies:
'@melt-ui/svelte': '>= 0.29.0' '@melt-ui/svelte': '>= 0.29.0'
svelte: ^3.55.0 || ^4.0.0 || ^5.0.0-next.1 svelte: ^3.55.0 || ^4.0.0 || ^5.0.0-next.1
dependencies: dependencies:
'@melt-ui/svelte': 0.74.0(svelte@4.2.11) '@melt-ui/svelte': 0.74.1(svelte@4.2.11)
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.5 magic-string: 0.30.5
svelte: 4.2.11 svelte: 4.2.11
@ -1772,8 +1772,8 @@ packages:
svelte: 4.2.11 svelte: 4.2.11
dev: false dev: false
/@melt-ui/svelte@0.74.0(svelte@4.2.11): /@melt-ui/svelte@0.74.1(svelte@4.2.11):
resolution: {integrity: sha512-4rFhTkO34OK5lflvcj13/in1YQ+exsD2A7YSNIYw2iyNeBYFxOgM0n4qH5Y4Tf7vcekkkQ+SEqgiD3XNmVBHzw==} resolution: {integrity: sha512-5A6QrPGLKIr4s1kI0FZrD5QoZ+wnACF2MR77DvBo8icVyFmpY+WiwpnA3cAl+zEce9Er2Uj8KxPBEF8NSMdnqw==}
peerDependencies: peerDependencies:
svelte: '>=3 <5' svelte: '>=3 <5'
dependencies: dependencies:
@ -3465,7 +3465,7 @@ packages:
dotenv: 16.4.4 dotenv: 16.4.4
front-matter: 4.0.2 front-matter: 4.0.2
nanoid: 4.0.2 nanoid: 4.0.2
oslo: 1.1.1 oslo: 1.1.2
dev: false dev: false
/autoprefixer@10.4.17(postcss@8.4.35): /autoprefixer@10.4.17(postcss@8.4.35):
@ -5550,8 +5550,8 @@ packages:
'@node-rs/bcrypt': 1.9.2 '@node-rs/bcrypt': 1.9.2
dev: false dev: false
/oslo@1.1.1: /oslo@1.1.2:
resolution: {integrity: sha512-BuJp5GfBW3jkUDLb8VftXD9FRTfjnodMyVshSy6p4UP8PhQI8auTxAQlef9NdFe6JC4mEgZb/mhMJpfNVudDTA==} resolution: {integrity: sha512-l5tmumAHJy6wTfcxjwbPtcEn23lxnsBqbW1PqM11lqnQ79Q8Gzc7b3+OjRpLTbiqkfvhT4/BZ2Ooe8/QCJxyEg==}
dependencies: dependencies:
'@node-rs/argon2': 1.7.0 '@node-rs/argon2': 1.7.0
'@node-rs/bcrypt': 1.9.0 '@node-rs/bcrypt': 1.9.0

View file

@ -33,7 +33,6 @@ export const userSchema = z.object({
confirm_password: z confirm_password: z
.string({ required_error: 'Confirm Password is required' }) .string({ required_error: 'Confirm Password is required' })
.trim(), .trim(),
role: z.enum(['USER', 'ADMIN'], { required_error: 'You must have a role' }).default('USER'),
verified: z.boolean().default(false), verified: z.boolean().default(false),
token: z.string().optional(), token: z.string().optional(),
receiveEmail: z.boolean().default(false), receiveEmail: z.boolean().default(false),

View file

@ -1,9 +1,11 @@
import { type Actions, error, fail, redirect } from '@sveltejs/kit'; import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import { superValidate } from 'sveltekit-superforms/server'; import { superValidate } from 'sveltekit-superforms/server';
import prisma from '$lib/prisma';
import { modifyListGameSchema, type ListGame } from '$lib/config/zod-schemas.js'; import { modifyListGameSchema, type ListGame } from '$lib/config/zod-schemas.js';
import { search_schema } from '$lib/zodValidation.js'; import { search_schema } from '$lib/zodValidation.js';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import db from '$lib/drizzle';
import { and, eq } from 'drizzle-orm';
import { collection_items, collections, games } from '../../../../schema';
export const load: PageServerLoad = async ({ fetch, url, locals }) => { export const load: PageServerLoad = async ({ fetch, url, locals }) => {
const user = locals.user; const user = locals.user;
@ -28,10 +30,8 @@ export const load: PageServerLoad = async ({ fetch, url, locals }) => {
const listManageForm = await superValidate(modifyListGameSchema); const listManageForm = await superValidate(modifyListGameSchema);
try { try {
let collection = await prisma.collection.findUnique({ const collection = await db.query.collections.findFirst({
where: { where: eq(collections.user_id, user.id)
user_id: user.id
}
}); });
console.log('collection', collection); console.log('collection', collection);
@ -45,27 +45,25 @@ export const load: PageServerLoad = async ({ fetch, url, locals }) => {
// }); // });
} }
let collection_items = await prisma.collectionItem.findMany({ const collectionItems = await db.query.collection_items.findMany({
where: { where: eq(collection_items.collection_id, collection.id),
collection_id: collection.id with: {
},
include: {
game: { game: {
select: { columns: {
id: true, id: true,
name: true, name: true,
thumb_url: true thumb_url: true
} }
} }
}, },
skip, offset: skip,
take: limit limit
}); });
console.log('collection_items', collection_items); console.log('collection_items', collectionItems);
let collectionItems: ListGame[] = []; const items: ListGame[] = [];
for (const item of collection_items) { for (const item of collectionItems) {
console.log('item', item); console.log('item', item);
const game = item.game; const game = item.game;
if (game) { if (game) {
@ -77,14 +75,14 @@ export const load: PageServerLoad = async ({ fetch, url, locals }) => {
times_played: item.times_played, times_played: item.times_played,
in_collection: true in_collection: true
}; };
collectionItems.push(collectionItem); items.push(collectionItem);
} }
} }
return { return {
searchForm, searchForm,
listManageForm, listManageForm,
collection: collectionItems collection: items
}; };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -108,10 +106,8 @@ export const actions: Actions = {
} }
const user = event.locals.user; const user = event.locals.user;
let game = await prisma.game.findUnique({ const game = await db.query.games.findFirst({
where: { where: eq(games.id, form.data.id)
id: form.data.id
}
}); });
if (!game) { if (!game) {
@ -125,10 +121,8 @@ export const actions: Actions = {
} }
try { try {
const collection = await prisma.collection.findUnique({ const collection = await db.query.collections.findFirst({
where: { where: eq(collections.user_id, user.id)
user_id: user.id
}
}); });
if (!collection) { if (!collection) {
@ -136,12 +130,10 @@ export const actions: Actions = {
return error(404, 'Wishlist not found'); return error(404, 'Wishlist not found');
} }
await prisma.collectionItem.create({ await db.insert(collection_items).values({
data: { game_id: game.id,
game_id: game.id, collection_id: collection.id,
collection_id: collection.id, times_played: 0
times_played: 0
}
}); });
return { return {
@ -175,10 +167,8 @@ export const actions: Actions = {
throw fail(401); throw fail(401);
} }
let game = await prisma.game.findUnique({ let game = await db.query.games.findFirst({
where: { where: eq(games.id, form.data.id)
id: form.data.id
}
}); });
if (!game) { if (!game) {
@ -187,10 +177,8 @@ export const actions: Actions = {
} }
try { try {
const collection = await prisma.collection.findUnique({ const collection = await db.query.collections.findFirst({
where: { where: eq(collections.user_id, locals.user.id)
user_id: locals.user.id
}
}); });
if (!collection) { if (!collection) {
@ -198,12 +186,10 @@ export const actions: Actions = {
return error(404, 'Collection not found'); return error(404, 'Collection not found');
} }
await prisma.collectionItem.delete({ await db.delete(collection_items).where(and(
where: { eq(collection_items.collection_id, collection.id),
collection_id: collection.id, eq(collection_items.game_id, game.id)
game_id: game.id ));
}
});
return { return {
form form

View file

@ -1,11 +1,11 @@
import { fail, redirect, type Actions } from "@sveltejs/kit"; import { fail, redirect, type Actions } from "@sveltejs/kit";
import { eq } from "drizzle-orm";
import { setError, superValidate } from 'sveltekit-superforms/server'; import { setError, superValidate } from 'sveltekit-superforms/server';
import { Argon2id } from "oslo/password"; import { Argon2id } from "oslo/password";
import db from "$lib/drizzle";
import { changeUserPasswordSchema } from '$lib/config/zod-schemas.js'; import { changeUserPasswordSchema } from '$lib/config/zod-schemas.js';
import { lucia } from '$lib/server/auth.js'; import { lucia } from '$lib/server/auth.js';
import type { PageServerLoad } from "./$types"; import type { PageServerLoad } from "./$types";
import db from "$lib/drizzle";
import { eq } from "drizzle-orm";
import { users } from "../../../../../schema"; import { users } from "../../../../../schema";
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {

View file

@ -52,3 +52,9 @@
<Button type="submit" class="w-full">Change Password</Button> <Button type="submit" class="w-full">Change Password</Button>
</div> </div>
</form> </form>
<style lang="postcss">
form {
width: 20rem;
}
</style>

View file

@ -1,9 +1,11 @@
import { fail, type Actions } from '@sveltejs/kit'; import { fail, type Actions } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import { message, setError, superValidate } from 'sveltekit-superforms/server'; import { message, setError, superValidate } from 'sveltekit-superforms/server';
import { redirect } from 'sveltekit-flash-message/server'; import { redirect } from 'sveltekit-flash-message/server';
import { userSchema } from '$lib/config/zod-schemas'; import { userSchema } from '$lib/config/zod-schemas';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import prisma from '$lib/prisma'; import { users } from '../../../../schema';
import db from '$lib/drizzle';
const profileSchema = userSchema.pick({ const profileSchema = userSchema.pick({
firstName: true, firstName: true,
@ -51,17 +53,24 @@ export const actions: Actions = {
const user = event.locals.user; const user = event.locals.user;
await prisma.user.update({ const newUsername = form.data.username;
where: { const existingUser = await db.query.users.findFirst({
id: user.id where: eq(users.username, newUsername)
}, });
data: {
firstName: form.data.firstName, if (existingUser && existingUser.id !== user.id) {
lastName: form.data.lastName, return setError(form, 'username', 'That username is already taken');
}
await db
.update(users)
.set({
first_name: form.data.firstName,
last_name: form.data.lastName,
email: form.data.email, email: form.data.email,
username: form.data.username username: form.data.username
} })
}); .where(eq(users.id, user.id));
if (user.email !== form.data.email) { if (user.email !== form.data.email) {
// Send email to confirm new email? // Send email to confirm new email?

View file

@ -89,6 +89,6 @@
<style lang="postcss"> <style lang="postcss">
form { form {
min-width: 300px;; width: 20rem;
} }
</style> </style>

View file

@ -1,7 +1,9 @@
import { error, redirect, type Actions } from '@sveltejs/kit'; import { error, redirect, type Actions } from '@sveltejs/kit';
import { superValidate } from 'sveltekit-superforms/server'; import { superValidate } from 'sveltekit-superforms/server';
import prisma from '$lib/prisma';
import { modifyListGameSchema } from '$lib/config/zod-schemas.js'; import { modifyListGameSchema } from '$lib/config/zod-schemas.js';
import db from '$lib/drizzle.js';
import { and, eq } from 'drizzle-orm';
import { games, wishlist_items, wishlists } from '../../../../schema.js';
export async function load({ params, locals }) { export async function load({ params, locals }) {
if (!locals.user) { if (!locals.user) {
@ -11,33 +13,31 @@ export async function load({ params, locals }) {
console.log('Wishlist load User id', locals.user.id); console.log('Wishlist load User id', locals.user.id);
try { try {
const wishlist = await prisma.wishlist.findUnique({ const wishlist = await db.query.wishlists.findFirst({
where: { where: eq(wishlists.user_id, locals.user.id)
user_id: locals.user.id
},
include: {
items: {
include: {
game: {
select: {
id: true,
name: true,
thumb_url: true
}
}
}
}
}
}); });
if (!wishlist) { if (!wishlist) {
redirect(302, '/404'); redirect(302, '/404');
} }
const items = await db.query.wishlist_items.findMany({
where: eq(wishlist_items.wishlist_id, wishlist.id),
with: {
game: {
columns: {
id: true,
name: true,
thumb_url: true
}
}
}
});
console.log('wishlist', wishlist); console.log('wishlist', wishlist);
return { return {
items: wishlist?.items items
}; };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -56,10 +56,8 @@ export const actions: Actions = {
redirect(302, '/login'); redirect(302, '/login');
} }
const game = await prisma.game.findUnique({ const game = await db.query.games.findFirst({
where: { where: eq(games.id, form.data.id)
id: form.data.id
}
}); });
if (!game) { if (!game) {
@ -73,10 +71,8 @@ export const actions: Actions = {
} }
if (game) { if (game) {
const wishlist = await prisma.wishlist.findUnique({ const wishlist = await db.query.wishlists.findFirst({
where: { where: eq(wishlists.user_id, locals.user.id)
user_id: locals.user.id
}
}); });
if (!wishlist) { if (!wishlist) {
@ -84,11 +80,9 @@ export const actions: Actions = {
return error(404, 'Wishlist not found'); return error(404, 'Wishlist not found');
} }
await prisma.wishlistItem.create({ await db.insert(wishlist_items).values({
data: {
game_id: game.id, game_id: game.id,
wishlist_id: wishlist.id wishlist_id: wishlist.id
}
}); });
} }
@ -124,10 +118,8 @@ export const actions: Actions = {
redirect(302, '/login'); redirect(302, '/login');
} }
const game = await prisma.game.findUnique({ const game = await db.query.games.findFirst({
where: { where: eq(games.id, form.data.id)
id: form.data.id
}
}); });
if (!game) { if (!game) {
@ -141,10 +133,8 @@ export const actions: Actions = {
} }
if (game) { if (game) {
const wishlist = await prisma.wishlist.findUnique({ const wishlist = await db.query.wishlists.findFirst({
where: { where: eq(wishlists.user_id, locals.user.id)
user_id: locals.user.id
}
}); });
if (!wishlist) { if (!wishlist) {
@ -152,12 +142,10 @@ export const actions: Actions = {
return error(404, 'Wishlist not found'); return error(404, 'Wishlist not found');
} }
await prisma.wishlistItem.delete({ await db.delete(wishlist_items).where(and(
where: { eq(wishlist_items.wishlist_id, wishlist.id),
wishlist_id: wishlist.id, eq(wishlist_items.game_id, game.id)
game_id: game.id ));
}
});
} }
return { return {

View file

@ -70,14 +70,6 @@ export const actions: Actions = {
ip_address: locals.ip ip_address: locals.ip
}); });
sessionCookie = lucia.createSessionCookie(session.id); sessionCookie = lucia.createSessionCookie(session.id);
await db.insert(collections).values({
user_id: user.id
}).onDuplicateKeyUpdate({ set: { user_id: sql`user_id` } });
await db.insert(wishlists).values({
user_id: user.id
}).onDuplicateKeyUpdate({ set: { user_id: sql`user_id` } });
} catch (e) { } catch (e) {
// TODO: need to return error message to the client // TODO: need to return error message to the client
console.error(e); console.error(e);

View file

@ -173,6 +173,7 @@ export const collection_items = pgTable('collection_items', {
}) })
.notNull() .notNull()
.references(() => games.id, { onDelete: 'cascade' }), .references(() => games.id, { onDelete: 'cascade' }),
times_played: integer('times_played').default(0),
created_at: timestamp('created_at', { created_at: timestamp('created_at', {
withTimezone: true, withTimezone: true,
mode: 'date', mode: 'date',
@ -223,7 +224,7 @@ export const wishlists_relations = relations(wishlists, ({ one }) => ({
user: one(users, { user: one(users, {
fields: [wishlists.user_id], fields: [wishlists.user_id],
references: [users.id] references: [users.id]
}) }),
})); }));
export const wishlist_items = pgTable('wishlist_items', { export const wishlist_items = pgTable('wishlist_items', {