diff --git a/server/server-route.ts b/server/server-route.ts index 2393726..af27659 100755 --- a/server/server-route.ts +++ b/server/server-route.ts @@ -13,8 +13,7 @@ export type ServerContext = { }; }; -export interface SessionContext> - extends ServerContext { +export interface SessionContext extends ServerContext { session: ServerSession; } @@ -92,7 +91,10 @@ export const useServerRouter = >( if (route.opt && route.opt?.request_as === "raw") { result = await route.handler.default.bind(arg)(); } else { - const params = await req.json(); + let params = []; + try { + params = await req.json(); + } catch (e) {} result = await route.handler.default.bind(arg)(...params); } diff --git a/server/server-session.ts b/server/server-session.ts index eccec46..4b86026 100755 --- a/server/server-session.ts +++ b/server/server-session.ts @@ -8,7 +8,7 @@ import { SingleSession, } from "./session/session-store"; -export type ServerSession> = SessionStore & { +export type ServerSession = SessionStore & { current?: SingleSession; }; @@ -17,11 +17,12 @@ type SessionServerHandler = { handle: (arg: ServerContext) => Promise; }; -export const createSessionServer = >(arg: { +export const createSessionServer = (arg: { encrypt?: boolean; router?: ReturnType; + site_id?: string; }): SessionServerHandler => { - const server_session = newSessionStore(); + const server_session = newSessionStore(arg.site_id); const server_handler: SessionServerHandler = { async cleanup() {}, diff --git a/server/session/schema.ts b/server/session/schema.ts index bdce7d3..28fd59e 100755 --- a/server/session/schema.ts +++ b/server/session/schema.ts @@ -5,15 +5,16 @@ import { select } from "lib/preset/login/utils/select"; export const session = sqliteTable( "session", { - session_id: text("session_id") + sid: text("sid") .notNull() .primaryKey() .$defaultFn(() => createId()), + uid: text("uid").notNull(), created_at: integer("created_at", { mode: "timestamp_ms" }).default( new Date() ), active: integer("active", { mode: "boolean" }), - data: text("data", { mode: "json" }), + data: text("data", { mode: "json" }).notNull(), expired_at: integer("expired_at", { mode: "timestamp_ms" }), }, (table) => { @@ -26,7 +27,7 @@ export const session = sqliteTable( export const track = sqliteTable( "track", { - track_id: text("track_id") + id: text("id") .notNull() .primaryKey() .$defaultFn(() => createId()), diff --git a/server/session/session-store.ts b/server/session/session-store.ts index 9153420..1fb4a2b 100755 --- a/server/session/session-store.ts +++ b/server/session/session-store.ts @@ -1,57 +1,153 @@ -export type NewSessionData> = T & { +/// +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 { uid: string; role: string; + data?: T; expired_at?: number; -}; +} -export type SessionData> = NewSessionData & { +export interface SessionData extends NewSessionData { sid: string; + active: boolean; created_at: number; -}; +} -export type SingleSession> = { - data: T; - track: (arg: { path: string }) => Promise; - destroy: () => Promise; -}; +export interface SingleSession extends SessionData { + track: (arg: { path: string }) => void; + destroy: () => boolean; +} type FilterSessionArg = { uid: string; sid: string; role: string; + active: boolean; created_at: { gt?: number; lt?: number }; expired_at: { gt?: number; lt?: number }; }; -export type SessionStore> = { - create: ( - data: Record & { - uid: string; - role: string; - expired_at?: number; - } - ) => Promise>; - findMany: (arg: Partial) => Promise[]>; - findFirst: ( - arg: Partial - ) => Promise>; +export type SessionStore = { + create: (arg: { + uid: string; + role: string; + data?: T; + expired_at?: number; + }) => SingleSession; + findMany: (arg?: Partial) => SingleSession[]; + findFirst: (arg?: Partial) => null | SingleSession; }; -export const newSessionStore = () => { +export const newSessionStore = (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>>, + }; + + 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 = >(data: any) => { + if (!data) return null; + + const row: SingleSession = { + ...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 { - async create(data) { - const new_session: SingleSession = { - data: { ...data, created_at: Date.now() } as T, - async destroy() {}, - async track(arg) {}, - }; - return new_session; + create(data) { + return createSingleStore( + db.session + .insert(session) + .values({ + uid: data.uid, + data, + active: true, + expired_at: data.expired_at + ? new Date(data.expired_at * 1000) + : undefined, + }) + .returning() + .get() + ); }, - async findFirst(arg) { - return null; + findFirst(arg) { + 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) { - return []; + findMany(arg) { + 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; }; diff --git a/server/utils/dir.ts b/server/utils/dir.ts new file mode 100755 index 0000000..b9f5fc5 --- /dev/null +++ b/server/utils/dir.ts @@ -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); + }, +};