Implement login

This commit is contained in:
orosmatthew 2023-04-28 13:42:16 -04:00
parent 39cf332500
commit 290ab487a1
8 changed files with 108 additions and 2 deletions

16
web/package-lock.json generated
View File

@ -15,6 +15,7 @@
"diff2html": "^3.4.35", "diff2html": "^3.4.35",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"prisma": "^4.13.0", "prisma": "^4.13.0",
"uuid": "^9.0.0",
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
"devDependencies": { "devDependencies": {
@ -22,6 +23,7 @@
"@sveltejs/kit": "^1.15.9", "@sveltejs/kit": "^1.15.9",
"@types/bootstrap": "^5.2.6", "@types/bootstrap": "^5.2.6",
"@types/diff": "^5.0.3", "@types/diff": "^5.0.3",
"@types/uuid": "^9.0.1",
"@typescript-eslint/eslint-plugin": "^5.59.1", "@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1", "@typescript-eslint/parser": "^5.59.1",
"eslint": "^8.39.0", "eslint": "^8.39.0",
@ -826,6 +828,12 @@
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
"dev": true "dev": true
}, },
"node_modules/@types/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.59.1", "version": "5.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz",
@ -3105,6 +3113,14 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "4.3.3", "version": "4.3.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz",

View File

@ -16,6 +16,7 @@
"@sveltejs/kit": "^1.15.9", "@sveltejs/kit": "^1.15.9",
"@types/bootstrap": "^5.2.6", "@types/bootstrap": "^5.2.6",
"@types/diff": "^5.0.3", "@types/diff": "^5.0.3",
"@types/uuid": "^9.0.1",
"@typescript-eslint/eslint-plugin": "^5.59.1", "@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1", "@typescript-eslint/parser": "^5.59.1",
"eslint": "^8.39.0", "eslint": "^8.39.0",
@ -38,6 +39,7 @@
"diff2html": "^3.4.35", "diff2html": "^3.4.35",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"prisma": "^4.13.0", "prisma": "^4.13.0",
"uuid": "^9.0.0",
"zod": "^3.21.4" "zod": "^3.21.4"
} }
} }

View File

@ -8,9 +8,17 @@ datasource db {
} }
model User { model User {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
username String @unique username String @unique
password String password String
sessions Session[]
}
model Session {
token String @id
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
userId Int
} }
enum SubmissionState { enum SubmissionState {

57
web/src/hooks.server.ts Normal file
View File

@ -0,0 +1,57 @@
import { redirect, type Handle } from '@sveltejs/kit';
import { db } from '$lib/server/prisma';
import type { Session } from '@prisma/client';
const sessionExpireMilliseconds = 1000 * 60 * 60 * 24; // 24 hours
function isSessionExpired(session: Session): boolean {
return session.createdAt.valueOf() + sessionExpireMilliseconds < new Date().valueOf();
}
async function removeExpiredSessions(userId: number) {
const sessions: Session[] = await db.session.findMany({ where: { userId: userId } });
sessions.forEach(async (session) => {
if (isSessionExpired(session)) {
await db.session.delete({ where: { token: session.token } });
}
});
}
export const handle = (async ({ event, resolve }) => {
if (event.url.pathname.startsWith('/login')) {
if (event.cookies.get('token')) {
const session = await db.session.findUnique({ where: { token: event.cookies.get('token') } });
if (session) {
removeExpiredSessions(session.userId);
if (!isSessionExpired(session)) {
throw redirect(302, '/admin/reviews');
} else {
event.cookies.delete('token');
}
} else {
const res = resolve(event);
return res;
}
}
}
if (event.url.pathname.startsWith('/admin')) {
if (event.cookies.get('token')) {
const session = await db.session.findUnique({ where: { token: event.cookies.get('token') } });
if (session) {
removeExpiredSessions(session.userId);
if (!isSessionExpired(session)) {
const res = await resolve(event);
return res;
} else {
event.cookies.delete('token');
}
} else {
throw redirect(302, '/login');
}
} else {
throw redirect(302, '/login');
}
}
const res = await resolve(event);
return res;
}) satisfies Handle;

View File

@ -6,6 +6,8 @@
<h1 class="mb-4">Reviews</h1> <h1 class="mb-4">Reviews</h1>
<a href="/logout" class="mb-2 btn btn-outline-primary">Logout</a>
<ul class="list-group"> <ul class="list-group">
{#each data.reviewList as review} {#each data.reviewList as review}
<a href="/admin/diff" class="list-group-item list-group-item-action">{review}</a> <a href="/admin/diff" class="list-group-item list-group-item-action">{review}</a>

View File

@ -1,5 +1,6 @@
import type { Actions } from '@sveltejs/kit'; import type { Actions } from '@sveltejs/kit';
import { db } from '$lib/server/prisma'; import { db } from '$lib/server/prisma';
import * as UUID from 'uuid';
export const actions = { export const actions = {
login: async ({ cookies, request }) => { login: async ({ cookies, request }) => {
@ -14,6 +15,9 @@ export const actions = {
return { success: false }; return { success: false };
} }
if (user.password === password) { if (user.password === password) {
const uuid: string = UUID.v4();
await db.session.create({ data: { token: uuid, userId: user.id } });
cookies.set('token', uuid);
return { success: true }; return { success: true };
} }
return { success: false }; return { success: false };

View File

@ -0,0 +1,16 @@
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { db } from '$lib/server/prisma';
export const load = (async ({ cookies }) => {
if (!cookies.get('token')) {
throw redirect(302, '/login');
}
try {
await db.session.delete({ where: { token: cookies.get('token') } });
} catch {
throw redirect(302, '/login');
}
cookies.delete('token');
throw redirect(302, '/login');
}) satisfies PageServerLoad;

View File

@ -0,0 +1 @@
<h1>Logging Out</h1>