prasi-bun/pkgs/core/utils/session.ts

107 lines
2.6 KiB
TypeScript

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<T> = { id: string; expired: number; data: T };
export type SRVCache<T> = ReturnType<typeof createCache<T>>;
export const createCache = <T>() => ({
lmdb: null as unknown as RootDatabase<SessionEntry<T>>,
cookieKey: "",
async init(arg: { cookieKey: string; dbname?: string }) {
const dbpath = dir(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<SessionEntry<T>> {
const s = {
id: cuid(),
expired: expired ? expired.getTime() / 1000 : 0,
data,
};
await self(this).lmdb.put(s.id, s);
return s;
},
get<T>(req: string | Request): null | SessionEntry<T> {
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<T>;
if (s) {
if (s.expired !== 0 && Date.now() / 1000 > s.expired) {
return null;
}
return s;
}
return null;
},
async set(id: string, data: any): Promise<SessionEntry<T>> {
await self(this).lmdb.put(id, data);
return data;
},
del(id: string) {
return self(this).lmdb.remove(id);
},
keys() {
return new Promise<lmdb.Key[]>((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();
const self = (me: Session) => me;
type Session = typeof session;
export function parseCookies(cookieHeader: string) {
const list: Record<string, string> = {};
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;
}