import { createId } from "@paralleldrive/cuid2"; import { dirAsync } from "fs-jetpack"; import lmdb, { RootDatabase, open } from "lmdb"; import { dirname, join } from "path"; import { g } from "./global"; import { dir } from "./dir"; const cuid = createId; type SessionEntry = { id: string; expired: number; data: T }; export type SRVCache = ReturnType>; export const createCache = () => ({ lmdb: null as unknown as RootDatabase>, cookieKey: "", async init(arg: { cookieKey: string; dbname?: string }) { const dbpath = dir.path(join(g.datadir, (arg.dbname || "session") + ".lmdb")); await dirAsync(dirname(dbpath)); self(this).lmdb = open({ path: dbpath, compression: true, }); self(this).cookieKey = arg.cookieKey; }, async new(data: any, expired?: Date): Promise> { const s = { id: cuid(), expired: expired ? expired.getTime() / 1000 : 0, data, }; await self(this).lmdb.put(s.id, s); return s; }, get(req: string | Request): null | SessionEntry { let id = ""; if (typeof req === "string") { id = req; } else { const cookie = req.headers.get("cookie"); if (cookie) { id = parseCookies(cookie)[self(this).cookieKey]; } } if (!id) { return null; } const s = self(this).lmdb.get(id) as unknown as SessionEntry; if (s) { if (s.expired !== 0 && Date.now() / 1000 > s.expired) { return null; } return s; } return null; }, async set(id: string, data: any): Promise> { await self(this).lmdb.put(id, data); return data; }, del(id: string) { return self(this).lmdb.remove(id); }, keys() { return new Promise((resolve) => { const keys: lmdb.Key[] = []; self(this) .lmdb.getKeys() .forEach((e) => { keys.push(e); }); resolve(keys); }); }, clear() { self(this).lmdb.clearSync(); }, count() { return self(this).lmdb.getCount(); }, }); export const session = createCache(); if (session.lmdb === null) { session.init({ cookieKey: "prasi-sid" }); } const self = (me: Session) => me; type Session = typeof session; export function parseCookies(cookieHeader: string) { const list: Record = {}; if (!cookieHeader) return list; cookieHeader.split(`;`).forEach(function (cookie) { let [name, ...rest] = cookie.split(`=`); name = name?.trim(); if (!name) return; const value = rest.join(`=`).trim(); if (!value) return; list[name] = decodeURIComponent(value); }); return list; }