feat(session): under the hood organisation and consolidation

This commit is contained in:
DecDuck
2024-11-16 18:20:14 +11:00
parent 5358f1f52c
commit 26a31f6d56
3 changed files with 62 additions and 53 deletions

View File

@ -2,15 +2,20 @@ import { H3Event, Session } from "h3";
import createMemorySessionProvider from "./memory"; import createMemorySessionProvider from "./memory";
import { SessionProvider } from "./types"; import { SessionProvider } from "./types";
import prisma from "../db/database"; import prisma from "../db/database";
import { v4 as uuidv4 } from "uuid";
import moment from "moment";
/* /*
This is a poorly organised implemention. This implementation may need work.
It exposes an API that should stay static, but there are plenty of opportunities for optimisation/organisation under the hood It exposes an API that should stay static, but there are plenty of opportunities for optimisation/organisation under the hood
*/ */
const userSessionKey = "_userSession"; const userSessionKey = "_userSession";
const userIdKey = "_userId"; const userIdKey = "_userId";
const dropTokenCookie = "drop-token";
const normalSessionLength = [31, "days"];
const extendedSessionLength = [1, "year"];
export class SessionHandler { export class SessionHandler {
private sessionProvider: SessionProvider; private sessionProvider: SessionProvider;
@ -20,33 +25,63 @@ export class SessionHandler {
this.sessionProvider = createMemorySessionProvider(); this.sessionProvider = createMemorySessionProvider();
} }
private getSessionToken(h3: H3Event) {
const cookie = getCookie(h3, dropTokenCookie);
return cookie;
}
private async createSession(h3: H3Event, extend = false) {
const token = uuidv4();
const expiry = moment().add(
...(extend ? extendedSessionLength : normalSessionLength)
);
setCookie(h3, dropTokenCookie, token, { expires: expiry.toDate() });
this.sessionProvider.setSession(dropTokenCookie, {});
return token;
}
async getSession<T extends Session>(h3: H3Event) { async getSession<T extends Session>(h3: H3Event) {
const token = this.getSessionToken(h3);
if (!token) return undefined;
const data = await this.sessionProvider.getSession<{ [userSessionKey]: T }>( const data = await this.sessionProvider.getSession<{ [userSessionKey]: T }>(
h3 token
); );
if (!data) return undefined; if (!data) return undefined;
return data[userSessionKey]; return data[userSessionKey];
} }
async setSession(h3: H3Event, data: any, expend = false) { async setSession(h3: H3Event, data: any, extend = false) {
const token =
this.getSessionToken(h3) ?? (await this.createSession(h3, extend));
const result = await this.sessionProvider.updateSession( const result = await this.sessionProvider.updateSession(
h3, token,
userSessionKey, userSessionKey,
data data
); );
if (!result) {
const toCreate = { [userSessionKey]: data }; return result;
await this.sessionProvider.setSession(h3, toCreate, expend);
}
} }
async clearSession(h3: H3Event) { async clearSession(h3: H3Event) {
await this.sessionProvider.clearSession(h3); const token = this.getSessionToken(h3);
if (!token) return false;
await this.sessionProvider.clearSession(token);
return true;
} }
async getUserId(h3: H3Event) { async getUserId(h3: H3Event, tag: string | boolean = false) {
const token = this.getSessionToken(h3);
if (!token) return undefined;
const session = await this.sessionProvider.getSession<{ const session = await this.sessionProvider.getSession<{
[userIdKey]: string | undefined; [userIdKey]: string | undefined;
}>(h3); }>(token);
if (tag)
console.log(`${tag} ${JSON.stringify(h3)} ${JSON.stringify(session)}`);
if (!session) return undefined; if (!session) return undefined;
return session[userIdKey]; return session[userIdKey];
@ -61,15 +96,14 @@ export class SessionHandler {
} }
async setUserId(h3: H3Event, userId: string, extend = false) { async setUserId(h3: H3Event, userId: string, extend = false) {
const token =
this.getSessionToken(h3) ?? (await this.createSession(h3, extend));
const result = await this.sessionProvider.updateSession( const result = await this.sessionProvider.updateSession(
h3, token,
userIdKey, userIdKey,
userId userId
); );
if (!result) {
const toCreate = { [userIdKey]: userId };
await this.sessionProvider.setSession(h3, toCreate, extend);
}
} }
async getAdminUser(h3: H3Event) { async getAdminUser(h3: H3Event) {

View File

@ -1,43 +1,22 @@
import moment from "moment";
import { Session, SessionProvider } from "./types"; import { Session, SessionProvider } from "./types";
import { v4 as uuidv4 } from "uuid";
export default function createMemorySessionHandler() { export default function createMemorySessionHandler() {
const sessions: { [key: string]: Session } = {}; const sessions: { [key: string]: Session } = {};
const sessionCookieName = "drop-session";
const memoryProvider: SessionProvider = { const memoryProvider: SessionProvider = {
async setSession(h3, data, extend = false) { async setSession(token, data) {
const existingCookie = getCookie(h3, sessionCookieName); sessions[token] = data;
if (existingCookie) delete sessions[existingCookie]; // Clear any previous session
const cookie = uuidv4();
const expiry = moment().add(31, extend ? "month" : "day");
setCookie(h3, sessionCookieName, cookie, { expires: expiry.toDate() });
sessions[cookie] = data;
return true; return true;
}, },
async updateSession(h3, key, data) { async updateSession(token, key, data) {
const cookie = getCookie(h3, sessionCookieName); sessions[token] = Object.assign({}, sessions[token], { [key]: data });
if (!cookie) return false;
sessions[cookie] = Object.assign({}, sessions[cookie], { [key]: data });
return true; return true;
}, },
async getSession(h3) { async getSession(token) {
const cookie = getCookie(h3, sessionCookieName); return sessions[token] as any; // Wild type cast because we let the user specify types if they want
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) { async clearSession(token) {
const cookie = getCookie(h3, sessionCookieName); delete sessions[token];
if (!cookie) return;
delete sessions[cookie];
deleteCookie(h3, sessionCookieName);
}, },
}; };

View File

@ -3,12 +3,8 @@ import { H3Event } from "h3";
export type Session = { [key: string]: any }; export type Session = { [key: string]: any };
export interface SessionProvider { export interface SessionProvider {
setSession: ( setSession: (token: string, data: Session) => Promise<boolean>;
h3: H3Event, updateSession: (token: string, key: string, data: any) => Promise<boolean>;
data: Session, getSession: <T extends Session>(token: string) => Promise<T | undefined>;
extend?: boolean clearSession: (token: string) => Promise<void>;
) => 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>;
} }