diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3263057..81b08b4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1222,6 +1222,16 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -2683,6 +2693,13 @@ "wrappy": "1" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", + "optional": true, + "peer": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2724,6 +2741,104 @@ "is-reference": "^3.0.0" } }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "optional": true, + "peer": true, + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true, + "peer": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "optional": true, + "peer": true + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "optional": true, + "peer": true, + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==", + "optional": true, + "peer": true + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "optional": true, + "peer": true, + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "optional": true, + "peer": true, + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -2909,6 +3024,49 @@ "url": "https://github.com/sponsors/porsager" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "optional": true, + "peer": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prettier": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", @@ -3467,6 +3625,16 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "dev": true }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/stacktracey": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", @@ -4622,6 +4790,16 @@ } } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/xxhash-wasm": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz", diff --git a/frontend/src/app.d.ts b/frontend/src/app.d.ts index 37d6fd3..9bb7b51 100644 --- a/frontend/src/app.d.ts +++ b/frontend/src/app.d.ts @@ -1,10 +1,13 @@ // See https://kit.svelte.dev/docs/types#app + +import type { User } from '$lib/db/schema' + // for information about these interfaces declare global { namespace App { // interface Error {} interface Locals { - user: number | null + userObject: User } // interface PageData {} // interface Platform {} diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index f7539a2..ac3e3c4 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -1,8 +1,8 @@ -import { authenticateUser } from '$lib/server/auth' +import { getUserFromEvent } from '$lib/server/auth' import { redirect, type Handle } from '@sveltejs/kit' export const handle: Handle = async ({ event, resolve }) => { - event.locals.user = await authenticateUser(event) + const userObject = await getUserFromEvent(event) const pathname = event.url.pathname @@ -13,18 +13,21 @@ export const handle: Handle = async ({ event, resolve }) => { '/api/signup', ] - if (pathname === '/login' || pathname === 'signup') { - if (event.locals.user) { + if (allowedPath.includes(pathname)) { + if (userObject) { throw redirect(303, '/') } + const response = await resolve(event) + + return response } - if (!allowedPath.includes(pathname)) { - if (!event.locals.user) { - throw redirect(303, '/login') - } + if (!userObject) { + throw redirect(303, '/login') } + event.locals.userObject = userObject + const response = await resolve(event) return response diff --git a/frontend/src/lib/db/schema.ts b/frontend/src/lib/db/schema.ts index d89cbc8..b32a040 100644 --- a/frontend/src/lib/db/schema.ts +++ b/frontend/src/lib/db/schema.ts @@ -7,7 +7,7 @@ import { uuid, } from 'drizzle-orm/pg-core' -import { relations } from 'drizzle-orm' +import { relations, type InferSelectModel } from 'drizzle-orm' export const shortener = pgTable('shortener', { id: serial('id').primaryKey().notNull(), @@ -74,3 +74,5 @@ export const sessionRelations = relations(session, ({ one }) => ({ references: [user.id], }), })) + +export type User = InferSelectModel diff --git a/frontend/src/lib/server/auth.ts b/frontend/src/lib/server/auth.ts index f899ed6..7356793 100644 --- a/frontend/src/lib/server/auth.ts +++ b/frontend/src/lib/server/auth.ts @@ -21,19 +21,6 @@ export const getUserFromSessionToken = async (token: string) => { return session.user } -export const authenticateUser = async (event: RequestEvent) => { - const { cookies } = event - const sessionToken = cookies.get('token') - - if (!sessionToken) { - return null - } - - const user = await getUserFromSessionToken(sessionToken) - - return user?.id ?? null -} - export const logoutUser = async (token: string) => { const now = new Date() await db @@ -46,26 +33,10 @@ export const getUserFromEvent = async (event: RequestEvent) => { const token = event.cookies.get('token') if (!token) { - return { - success: false, - response: JSON.stringify({ - success: false, - message: 'Invalid User', - }), - } as const + return null } const user = await getUserFromSessionToken(token) - if (!user) { - return { - success: false, - response: JSON.stringify({ - success: false, - message: 'Invalid User', - }), - } as const - } - - return { success: true, user } as const + return user } diff --git a/frontend/src/lib/server/response.ts b/frontend/src/lib/server/response.ts new file mode 100644 index 0000000..b9d778e --- /dev/null +++ b/frontend/src/lib/server/response.ts @@ -0,0 +1,13 @@ +type SuccessResponse = { + success: true + data: any +} + +type ErrorResponse = { + success: false + message: string +} + +const generateResponse = (data: SuccessResponse | ErrorResponse) => { + return new Response(JSON.stringify(data)) +} diff --git a/frontend/src/routes/(app)/+layout.server.ts b/frontend/src/routes/(app)/+layout.server.ts index 5e57a43..27d4c37 100644 --- a/frontend/src/routes/(app)/+layout.server.ts +++ b/frontend/src/routes/(app)/+layout.server.ts @@ -1,19 +1,7 @@ -import { getUserFromSessionToken } from '$lib/server/auth' -import { redirect } from '@sveltejs/kit' import type { LayoutServerLoad } from './$types' export const load = (async (event) => { - const token = event.cookies.get('token') - - if (!token) { - throw redirect(303, '/') - } - - const user = await getUserFromSessionToken(token) - - if (!user) { - throw redirect(303, '/') - } + const user = event.locals.userObject return { shortener_url: process.env.SHORTENER_URL ?? 's.tzgyn.com', diff --git a/frontend/src/routes/api/shortener/+server.ts b/frontend/src/routes/api/shortener/+server.ts index 3cec11b..a2b6829 100644 --- a/frontend/src/routes/api/shortener/+server.ts +++ b/frontend/src/routes/api/shortener/+server.ts @@ -1,7 +1,6 @@ import { z } from 'zod' import type { RequestHandler } from './$types' import { db } from '$lib/db' -import { getUserFromSessionToken } from '$lib/server/auth' import { shortener } from '$lib/db/schema' import { nanoid } from 'nanoid' @@ -27,27 +26,7 @@ export const POST: RequestHandler = async (event) => { ) } - const token = event.cookies.get('token') - - if (!token) { - return new Response( - JSON.stringify({ - success: false, - message: 'Invalid User', - }), - ) - } - - const user = await getUserFromSessionToken(token) - - if (!user) { - return new Response( - JSON.stringify({ - success: false, - message: 'Invalid User', - }), - ) - } + const user = event.locals.userObject const code = nanoid(8) diff --git a/frontend/src/routes/api/shortener/[id]/+server.ts b/frontend/src/routes/api/shortener/[id]/+server.ts index 17c91a5..872aad1 100644 --- a/frontend/src/routes/api/shortener/[id]/+server.ts +++ b/frontend/src/routes/api/shortener/[id]/+server.ts @@ -1,9 +1,5 @@ import { db } from '$lib/db' -import { shortener } from '$lib/db/schema' -import { - getUserFromEvent, - getUserFromSessionToken, -} from '$lib/server/auth' +import { shortener as shortenerSchema } from '$lib/db/schema' import { and, eq } from 'drizzle-orm' import type { RequestHandler } from './$types' import { z } from 'zod' @@ -17,13 +13,7 @@ const updateShortenerSchema = z.object({ }) export const PUT: RequestHandler = async (event) => { - const data = await getUserFromEvent(event) - - if (!data.success) { - return new Response(data.response) - } - - const user = data.user + const user = event.locals.userObject const shortenerId = event.params.id const body = await event.request.json() @@ -40,12 +30,12 @@ export const PUT: RequestHandler = async (event) => { } await db - .update(shortener) + .update(shortenerSchema) .set({ link: updateShortener.data.link }) .where( and( - eq(shortener.code, shortenerId), - eq(shortener.userId, user.id), + eq(shortenerSchema.code, shortenerId), + eq(shortenerSchema.userId, user.id), ), ) @@ -54,33 +44,14 @@ export const PUT: RequestHandler = async (event) => { export const DELETE: RequestHandler = async (event) => { const shortenerId = event.params.id - const token = event.cookies.get('token') - - if (!token) { - return new Response( - JSON.stringify({ - success: false, - message: 'Invalid User', - }), - ) - } - const user = await getUserFromSessionToken(token) - - if (!user) { - return new Response( - JSON.stringify({ - success: false, - message: 'Invalid User', - }), - ) - } + const user = event.locals.userObject await db - .delete(shortener) + .delete(shortenerSchema) .where( and( - eq(shortener.code, shortenerId), - eq(shortener.userId, user.id), + eq(shortenerSchema.code, shortenerId), + eq(shortenerSchema.userId, user.id), ), ) return new Response(