feat: add server sessions

This commit is contained in:
Julien Oculi 2024-06-13 12:03:07 +02:00
parent c971542ec8
commit ca79b4a20d

142
src/session/mod.ts Normal file
View file

@ -0,0 +1,142 @@
import { getCookies } from '@std/http/cookie'
type SessionEntry<T extends unknown = unknown> = {
accessCount: number
flash: boolean
value: T
}
export class SessionStore {
static #store = new Map<string, Session>()
static createSession(): Session {
return new Session(this.#store)
}
static getSession(uuid: string): Session | undefined {
// Check session validity
const halfHour = 30 * 60 * 1_000
const maxAge = Date.now() - halfHour
const session = this.#store.get(uuid)
if (session === undefined) {
return undefined
}
if (session.timestamp < maxAge) {
session.destroy()
return undefined
}
return session
}
static getFromRequest(request: Request): Session | undefined {
const sessionId = getCookies(request.headers)['_SESSION'] ?? ''
return this.getSession(sessionId)
}
private constructor() {}
}
class Session {
#db = new Map<string, SessionEntry>()
#timestamp = Date.now()
#uuid = crypto.randomUUID()
#store: Map<string, Session>
constructor(store: Map<string, Session>) {
this.#store = store
store.set(this.#uuid, this)
//cleanup old sessions
const halfHour = 30 * 60 * 1_000
const maxAge = this.#timestamp - halfHour
for (const [uuid, session] of store.entries()) {
if (session.#timestamp < maxAge) {
store.delete(uuid)
}
}
}
#updateTimestamp() {
this.#timestamp = Date.now()
}
get uuid() {
return this.#uuid
}
get timestamp() {
return this.#timestamp
}
get<T extends unknown = unknown>(key: string): T | undefined {
this.#updateTimestamp()
const entry = this.#db.get(key)
// no entry
if (entry === undefined) return
// flash entry
if (entry.flash) {
this.#db.delete(key)
return entry.value as T
}
// normal entry
entry.accessCount++
this.#db.set(key, entry)
return entry.value as T
}
set<T extends unknown = unknown>(key: string, value: T): void {
this.#updateTimestamp()
const entry = this.#db.get(key) as SessionEntry<T> | undefined
// update or create
const newEntry: SessionEntry<T> = {
accessCount: entry?.accessCount ? entry.accessCount++ : 0,
flash: entry?.flash ?? false,
value,
}
this.#db.set(key, newEntry)
}
delete(key: string): boolean {
this.#updateTimestamp()
return this.#db.delete(key)
}
has(key: string): boolean {
this.#updateTimestamp()
return this.#db.has(key)
}
list(): [key: string, value: unknown][] {
this.#updateTimestamp()
const keys = [...this.#db.keys()]
return keys.map((key) => ([key, this.get(key)] as [string, unknown]))
}
flash<T extends unknown = unknown>(key: string, value: T) {
this.#updateTimestamp()
const entry = this.#db.get(key) as SessionEntry<T> | undefined
// update or create
const newEntry: SessionEntry<T> = {
accessCount: entry?.accessCount ? entry.accessCount++ : 0,
flash: true,
value,
}
this.#db.set(key, newEntry)
}
destroy() {
this.#db.clear()
this.#store.delete(this.uuid)
}
}
export type { Session }