This commit is contained in:
rizky 2024-08-22 21:22:12 -07:00
parent 0d732bccf4
commit 3cbc04fae4
5 changed files with 136 additions and 28 deletions

View File

@ -21,6 +21,7 @@
"@types/lodash.capitalize": "^4.2.9", "@types/lodash.capitalize": "^4.2.9",
"@types/lodash.get": "^4.4.9", "@types/lodash.get": "^4.4.9",
"@types/react": "^18.2.65", "@types/react": "^18.2.65",
"drizzle-orm": "^0.33.0",
"bun-types": "^1.1.24", "bun-types": "^1.1.24",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"rou3": "^0.5.1", "rou3": "^0.5.1",

View File

@ -1,7 +1,8 @@
import { _post } from "lib/utils/post"; import { _post } from "lib/utils/post";
import { addRoute, createRouter, findRoute } from "rou3"; import { addRoute, createRouter, findRoute } from "rou3";
import { ServerSession, SessionData } from "./server-session";
export type ServerArg = { export type ServerContext = {
req: Request; req: Request;
handle: (req: Request) => Promise<Response>; handle: (req: Request) => Promise<Response>;
mode: "dev" | "prod"; mode: "dev" | "prod";
@ -11,6 +12,11 @@ export type ServerArg = {
}; };
}; };
export interface SessionContext<T extends SessionData<any>>
extends ServerContext {
session: ServerSession<T>;
}
type RouteFn = (...arg: any[]) => Promise<any>; type RouteFn = (...arg: any[]) => Promise<any>;
type SingleRoute = [string, () => Promise<{ default: RouteFn }>]; type SingleRoute = [string, () => Promise<{ default: RouteFn }>];
@ -31,9 +37,7 @@ export const newServerRouter = <
return arg; return arg;
}; };
export const createClientForServer = < export const newClientRouter = <T extends ReturnType<typeof newServerRouter>>(
T extends ReturnType<typeof newServerRouter>
>(
router: T router: T
) => { ) => {
return new Proxy( return new Proxy(
@ -72,7 +76,7 @@ export const useServerRouter = <T extends ReturnType<typeof newServerRouter>>(
} }
return { return {
async handle(arg: ServerArg) { async handle(arg: ServerContext | SessionContext<any>) {
const { url, req } = arg; const { url, req } = arg;
const found = findRoute(rou, undefined, url.pathname); const found = findRoute(rou, undefined, url.pathname);
@ -94,7 +98,7 @@ export const useServerRouter = <T extends ReturnType<typeof newServerRouter>>(
if (typeof result === "object" && result instanceof Response) { if (typeof result === "object" && result instanceof Response) {
return result; return result;
} }
return new Response(JSON.stringify(result)); return new Response(JSON.stringify(result));
} }
return await arg.handle(arg.req); return await arg.handle(arg.req);

View File

@ -1,43 +1,100 @@
/// <reference types="bun-types" /> /// <reference types="bun-types" />
import { ServerArg, useServerRouter } from "./server-route"; import { ServerContext, useServerRouter } from "./server-route";
type ServerSession = { export type SessionData<T extends Record<string, any> | undefined> = T & {
handle: (arg: ServerArg) => Promise<Response>; uid: string;
role: string;
sid: string;
expired_at?: number;
created_at: number;
}; };
export const sessionServer = <T>(arg: { export type SingleSession<T extends SessionData<any>> = {
data: T;
track: (arg: { path: string }) => Promise<void>;
destroy: () => Promise<void>;
};
type FilterSessionArg = {
uid: string;
sid: string;
role: string;
created_at: { gt?: number; lt?: number };
expired_at: { gt?: number; lt?: number };
};
export type ServerSession<T extends SessionData<any>> = {
create: (
data: Record<string, any> & {
uid: string;
role: string;
expired_at?: number;
}
) => Promise<SingleSession<T>>;
findMany: (arg: Partial<FilterSessionArg>) => Promise<SingleSession<T>[]>;
findFirst: (
arg: Partial<FilterSessionArg>
) => Promise<null | SingleSession<T>>;
current?: SingleSession<T>;
};
type SessionServerHandler = {
cleanup: () => Promise<void>;
handle: (arg: ServerContext) => Promise<Response>;
};
export const createSessionServer = <T extends SessionData<any>>(arg: {
encrypt?: boolean; encrypt?: boolean;
router?: ReturnType<typeof useServerRouter>; router?: ReturnType<typeof useServerRouter>;
on: { }): SessionServerHandler => {
login: (arg: {
mode: "user-pass";
username: string;
password: string;
}) => Promise<false | T>;
};
}): ServerSession => {
const internal = { const internal = {
has_router: false, has_router: false,
router: null as null | ReturnType<typeof useServerRouter>, router: null as null | ReturnType<typeof useServerRouter>,
}; };
const server_session: Omit<ServerSession<T>, "current"> = {
async create(data) {
const new_session: SingleSession<T> = {
data: { ...data, created_at: Date.now() } as T,
async destroy() {},
async track(arg) {},
};
return new_session;
},
async findFirst(arg) {
return null;
},
async findMany(arg) {
return [];
},
};
if (typeof arg.router === "object" && arg.router instanceof Promise) { if (typeof arg.router === "object" && arg.router instanceof Promise) {
internal.has_router = true; internal.has_router = true;
arg.router.then((e) => { arg.router.then((e) => {
internal.router = e; internal.router = e;
}); });
} }
const s: ServerSession = {
const server_handler: SessionServerHandler = {
async cleanup() {},
async handle(server_arg) { async handle(server_arg) {
const { req, handle } = server_arg; const { req, handle } = server_arg;
if (internal.has_router && internal.router) { console.log("makaru", internal.has_router, internal.router);
return await internal.router.handle(server_arg); return new Response("fas");
} // if (internal.has_router && internal.router) {
// return await internal.router.handle({
// ...server_arg,
// session: {
// ...server_session,
// current: undefined,
// },
// });
// }
return handle(req); // return handle(req);
}, },
}; };
return s; return server_handler;
}; };

46
server/session/schema.ts Executable file
View File

@ -0,0 +1,46 @@
import { createId } from "@paralleldrive/cuid2";
import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
export const session = sqliteTable(
"session",
{
session_id: text("session_id")
.notNull()
.primaryKey()
.$defaultFn(() => createId()),
created_at: integer("created_at", { mode: "timestamp_ms" }).default(
new Date()
),
active: integer("active", { mode: "boolean" }),
data: text("data", { mode: "json" }),
expired_at: integer("expired_at", { mode: "timestamp_ms" }),
},
(table) => {
return {
expired_at_idx: index("expired_at_idx").on(table.expired_at),
};
}
);
export const track = sqliteTable(
"track",
{
track_id: text("track_id")
.notNull()
.primaryKey()
.$defaultFn(() => createId()),
created_at: integer("created_at", { mode: "timestamp_ms" }).default(
new Date()
),
session_id: text("session_id"),
url: text("url").notNull(),
referer: text("referer"),
user_ip: text("user_ip"),
data: text("data", { mode: "json" }),
tstamp: integer("tstamp", { mode: "timestamp_ms" }).default(new Date()),
},
(table) => {
return {
session_id: index("session_id").on(table.session_id),
};
}
);

View File

@ -1,15 +1,15 @@
import { SessionData } from "lib/server/server-session";
const w = window as unknown as { const w = window as unknown as {
_prasi_session: any; _prasi_session: any;
_prasi: { site_id: string }; _prasi: { site_id: string };
}; };
export type SessionData = Record<string, any> & { role: string };
type SessionResponse<T> = type SessionResponse<T> =
| { active: false; reason: string } | { active: false; reason: string }
| { active: true; data: T; token: string }; | { active: true; data: T; token: string };
export const sessionClient = async <T extends SessionData>(arg: { export const sessionClient = async <T extends SessionData<any>>(arg: {
editorSampleData: T; editorSampleData: T;
auth: { auth: {
mode: "user-pass"; mode: "user-pass";
@ -70,7 +70,7 @@ export const sessionClient = async <T extends SessionData>(arg: {
return session; return session;
}; };
export type Session<T extends SessionData> = { export type Session<T extends SessionData<any>> = {
active: boolean; active: boolean;
id_site: string; id_site: string;
login: (arg: { username: string; password: string }) => Promise<void>; login: (arg: { username: string; password: string }) => Promise<void>;