fix server session

This commit is contained in:
rizky 2024-08-23 05:48:40 -07:00
parent e618c883d5
commit c500728f08
5 changed files with 159 additions and 42 deletions

View File

@ -13,8 +13,7 @@ export type ServerContext = {
}; };
}; };
export interface SessionContext<T extends SessionData<any>> export interface SessionContext<T> extends ServerContext {
extends ServerContext {
session: ServerSession<T>; session: ServerSession<T>;
} }
@ -92,7 +91,10 @@ export const useServerRouter = <T extends ReturnType<typeof newServerRouter>>(
if (route.opt && route.opt?.request_as === "raw") { if (route.opt && route.opt?.request_as === "raw") {
result = await route.handler.default.bind(arg)(); result = await route.handler.default.bind(arg)();
} else { } else {
const params = await req.json(); let params = [];
try {
params = await req.json();
} catch (e) {}
result = await route.handler.default.bind(arg)(...params); result = await route.handler.default.bind(arg)(...params);
} }

View File

@ -8,7 +8,7 @@ import {
SingleSession, SingleSession,
} from "./session/session-store"; } from "./session/session-store";
export type ServerSession<T extends SessionData<any>> = SessionStore<T> & { export type ServerSession<T> = SessionStore<T> & {
current?: SingleSession<T>; current?: SingleSession<T>;
}; };
@ -17,11 +17,12 @@ type SessionServerHandler = {
handle: (arg: ServerContext) => Promise<Response>; handle: (arg: ServerContext) => Promise<Response>;
}; };
export const createSessionServer = <T extends SessionData<any>>(arg: { export const createSessionServer = <T >(arg: {
encrypt?: boolean; encrypt?: boolean;
router?: ReturnType<typeof useServerRouter>; router?: ReturnType<typeof useServerRouter>;
site_id?: string;
}): SessionServerHandler => { }): SessionServerHandler => {
const server_session = newSessionStore<T>(); const server_session = newSessionStore<T>(arg.site_id);
const server_handler: SessionServerHandler = { const server_handler: SessionServerHandler = {
async cleanup() {}, async cleanup() {},

View File

@ -5,15 +5,16 @@ import { select } from "lib/preset/login/utils/select";
export const session = sqliteTable( export const session = sqliteTable(
"session", "session",
{ {
session_id: text("session_id") sid: text("sid")
.notNull() .notNull()
.primaryKey() .primaryKey()
.$defaultFn(() => createId()), .$defaultFn(() => createId()),
uid: text("uid").notNull(),
created_at: integer("created_at", { mode: "timestamp_ms" }).default( created_at: integer("created_at", { mode: "timestamp_ms" }).default(
new Date() new Date()
), ),
active: integer("active", { mode: "boolean" }), active: integer("active", { mode: "boolean" }),
data: text("data", { mode: "json" }), data: text("data", { mode: "json" }).notNull(),
expired_at: integer("expired_at", { mode: "timestamp_ms" }), expired_at: integer("expired_at", { mode: "timestamp_ms" }),
}, },
(table) => { (table) => {
@ -26,7 +27,7 @@ export const session = sqliteTable(
export const track = sqliteTable( export const track = sqliteTable(
"track", "track",
{ {
track_id: text("track_id") id: text("id")
.notNull() .notNull()
.primaryKey() .primaryKey()
.$defaultFn(() => createId()), .$defaultFn(() => createId()),

View File

@ -1,57 +1,153 @@
export type NewSessionData<T extends Record<string, any>> = T & { /// <reference types="bun-types" />
import Database from "bun:sqlite";
import { dir } from "../utils/dir";
import { mkdirSync } from "fs";
import { join } from "path";
import { BunSQLiteDatabase, drizzle } from "drizzle-orm/bun-sqlite";
import { session } from "./schema";
import { and, eq, sql } from "drizzle-orm";
export interface NewSessionData<T> {
uid: string; uid: string;
role: string; role: string;
data?: T;
expired_at?: number; expired_at?: number;
}; }
export type SessionData<T extends Record<string, any>> = NewSessionData<T> & { export interface SessionData<T> extends NewSessionData<T> {
sid: string; sid: string;
active: boolean;
created_at: number; created_at: number;
}; }
export type SingleSession<T extends SessionData<any>> = { export interface SingleSession<T> extends SessionData<T> {
data: T; track: (arg: { path: string }) => void;
track: (arg: { path: string }) => Promise<void>; destroy: () => boolean;
destroy: () => Promise<void>; }
};
type FilterSessionArg = { type FilterSessionArg = {
uid: string; uid: string;
sid: string; sid: string;
role: string; role: string;
active: boolean;
created_at: { gt?: number; lt?: number }; created_at: { gt?: number; lt?: number };
expired_at: { gt?: number; lt?: number }; expired_at: { gt?: number; lt?: number };
}; };
export type SessionStore<T extends SessionData<any>> = { export type SessionStore<T> = {
create: ( create: (arg: {
data: Record<string, any> & { uid: string;
uid: string; role: string;
role: string; data?: T;
expired_at?: number; expired_at?: number;
} }) => SingleSession<T>;
) => Promise<SingleSession<T>>; findMany: (arg?: Partial<FilterSessionArg>) => SingleSession<T>[];
findMany: (arg: Partial<FilterSessionArg>) => Promise<SingleSession<T>[]>; findFirst: (arg?: Partial<FilterSessionArg>) => null | SingleSession<T>;
findFirst: (
arg: Partial<FilterSessionArg>
) => Promise<null | SingleSession<T>>;
}; };
export const newSessionStore = <T>() => { export const newSessionStore = <T>(site_id?: string) => {
const db_path = site_id
? dir.data(`/code/${site_id}/site/sqlite`)
: dir.data(`/sqlite`);
mkdirSync(db_path, { recursive: true });
const path = { session: join(db_path, "session.db") };
const db = {
session: drizzle(new Database(path.session), { schema: { session } }),
track: {} as Record<string, BunSQLiteDatabase<Record<string, never>>>,
};
db.session.run(sql`
CREATE TABLE IF NOT EXISTS session (
sid text PRIMARY KEY NOT NULL,
uid text NOT NULL,
created_at integer NOT NULL DEFAULT current_timestamp,
active integer,
data text NOT NULL,
expired_at integer
);
CREATE INDEX IF NOT EXISTS expired_at_idx ON session (expired_at);
`);
// const track_ddl = sql`
// CREATE TABLE track (
// id text PRIMARY KEY NOT NULL,
// created_at integer DEFAULT current_timestamp,
// session_id text,
// url text NOT NULL,
// referer text,
// user_ip text,
// data text,
// tstamp integer DEFAULT current_timestamp
// );
// CREATE INDEX session_id ON track (session_id);`;
const createSingleStore = <E extends SessionData<T>>(data: any) => {
if (!data) return null;
const row: SingleSession<E> = {
...data,
destroy() {
try {
db.session.delete(session).where(eq(session.sid, row.sid)).run();
return true;
} catch (e) {
console.error(e);
return false;
}
},
track(arg) {},
};
return row;
};
return { return {
async create(data) { create(data) {
const new_session: SingleSession<T> = { return createSingleStore(
data: { ...data, created_at: Date.now() } as T, db.session
async destroy() {}, .insert(session)
async track(arg) {}, .values({
}; uid: data.uid,
return new_session; data,
active: true,
expired_at: data.expired_at
? new Date(data.expired_at * 1000)
: undefined,
})
.returning()
.get()
);
}, },
async findFirst(arg) { findFirst(arg) {
return null; return createSingleStore(
db.session
.select()
.from(session)
.where(
and(
...Object.entries(arg || {}).map(([k, v]) => {
return eq((session as any)[k], v);
})
)
)
.get()
);
}, },
async findMany(arg) { findMany(arg) {
return []; return db.session
.select()
.from(session)
.where(
and(
...Object.entries(arg || {}).map(([k, v]) => {
return eq((session as any)[k], v);
})
)
)
.all()
.map((e) => createSingleStore(e));
}, },
} as SessionStore<T>; } as SessionStore<T>;
}; };

17
server/utils/dir.ts Executable file
View File

@ -0,0 +1,17 @@
import { join } from "path";
const g = (typeof global !== "undefined" ? global : undefined) as unknown as {
datadir?: string;
mode?: string;
};
export const dir = {
data: (path: string) => {
const final_path = path
.split("/")
.filter((e) => e !== "..")
.join("/");
if (g.mode === "prod") return join(process.cwd(), "..", "data", final_path);
else return join(process.cwd(), "data", final_path);
},
};