initial commit

This commit is contained in:
DecDuck
2024-09-28 19:12:11 +10:00
commit e1a789fa36
28 changed files with 5669 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
import { PrismaClient } from '@prisma/client'
const prismaClientSingleton = () => {
return new PrismaClient()
}
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
export default prisma
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma
+11
View File
@@ -0,0 +1,11 @@
import bcrypt from 'bcrypt';
const rounds = 10;
export async function createHash(password: string) {
return bcrypt.hashSync(password, rounds);
}
export async function checkHash(password: string, hash: string) {
return bcrypt.compareSync(password, hash);
}
+64
View File
@@ -0,0 +1,64 @@
import { H3Event, Session } from "h3";
import createMemorySessionProvider from "./memory";
import { SessionProvider } from "./types";
import prisma from "../db/database";
/*
This is a poorly organised implemention.
It exposes an API that should stay static, but there are plenty of opportunities for optimisation/organisation under the hood
*/
const userSessionKey = "_userSession";
const userIdKey = "_userId";
export class SessionHandler {
private sessionProvider: SessionProvider;
constructor() {
// Create a new provider
this.sessionProvider = createMemorySessionProvider();
}
async getSession<T extends Session>(h3: H3Event) {
const data = await this.sessionProvider.getSession<{ [userSessionKey]: T }>(h3);
if (!data) return undefined;
return data[userSessionKey];
}
async setSession(h3: H3Event, data: any) {
const result = await this.sessionProvider.updateSession(h3, userSessionKey, data);
if (!result) {
const toCreate = { [userSessionKey]: data };
await this.sessionProvider.setSession(h3, toCreate);
}
}
async clearSession(h3: H3Event) {
await this.sessionProvider.clearSession(h3);
}
async getUserId(h3: H3Event) {
const session = await this.sessionProvider.getSession<{ [userIdKey]: string | undefined }>(h3);
if (!session) return undefined;
return session[userIdKey];
}
async getUser(h3: H3Event) {
const userId = await this.getUserId(h3);
if (!userId) return undefined;
const user = await prisma.user.findFirst({ where: { id: userId } });
return user;
}
async setUserId(h3: H3Event, userId: string) {
const result = await this.sessionProvider.updateSession(h3, userIdKey, userId);
if (!result) {
const toCreate = { [userIdKey]: userId };
await this.sessionProvider.setSession(h3, toCreate);
}
}
}
export default new SessionHandler();
+45
View File
@@ -0,0 +1,45 @@
import moment from "moment";
import { Session, SessionProvider } from "./types";
import { v4 as uuidv4 } from 'uuid';
export default function createMemorySessionHandler() {
const sessions: { [key: string]: Session } = {}
const sessionCookieName = "drop-session";
const memoryProvider: SessionProvider = {
async setSession(h3, data) {
const existingCookie = getCookie(h3, sessionCookieName);
if (existingCookie) delete sessions[existingCookie]; // Clear any previous session
const cookie = uuidv4();
const expiry = moment().add(31, 'day');
setCookie(h3, sessionCookieName, cookie, { expires: expiry.toDate() });
sessions[cookie] = data;
return true;
},
async updateSession(h3, key, data) {
const cookie = getCookie(h3, sessionCookieName);
if (!cookie) return false;
sessions[cookie] = Object.assign({}, sessions[cookie], { [key]: data });
return true;
},
async getSession(h3) {
const cookie = getCookie(h3, sessionCookieName);
if (!cookie) return undefined;
return sessions[cookie] as any; // Wild type cast because we let the user specify types if they want
},
async clearSession(h3) {
const cookie = getCookie(h3, sessionCookieName);
if (!cookie) return;
delete sessions[cookie];
deleteCookie(h3, sessionCookieName);
},
};
return memoryProvider;
}
+10
View File
@@ -0,0 +1,10 @@
import { H3Event } from "h3";
export type Session = { [key: string]: any };
export interface SessionProvider {
setSession: (h3: H3Event, data: Session) => Promise<boolean>;
updateSession: (h3: H3Event, key: string, data: any) => Promise<boolean>;
getSession: <T extends Session>(h3: H3Event) => Promise<T | undefined>;
clearSession: (h3: H3Event) => Promise<void>;
}