From 3e242c9e53137f09f3b322f9d6c7573d5d3b5385 Mon Sep 17 00:00:00 2001 From: rizky Date: Tue, 8 Oct 2024 00:34:19 -0700 Subject: [PATCH] checkpoint --- comps/form/field/type/TypeOTP.tsx | 15 +++-- preset/login/utils/register.ts | 3 +- preset/menu/Layout.tsx | 3 +- server/server-route.ts | 15 ++++- session/client-session.ts | 104 ++++++++++++++++++++++++++++++ session/server-session.ts | 56 ++++++++++++++++ session/type.ts | 11 +++- 7 files changed, 194 insertions(+), 13 deletions(-) create mode 100755 session/client-session.ts create mode 100755 session/server-session.ts diff --git a/comps/form/field/type/TypeOTP.tsx b/comps/form/field/type/TypeOTP.tsx index 703c3df..820710b 100755 --- a/comps/form/field/type/TypeOTP.tsx +++ b/comps/form/field/type/TypeOTP.tsx @@ -1,5 +1,5 @@ import { useLocal } from "lib/utils/use-local"; -import { FC } from "react"; +import { FC, useEffect } from "react"; import { FieldLocal, FieldProp, FMLocal } from "../../typings"; import { PropTypeInput } from "./TypeInput"; @@ -15,6 +15,13 @@ export const FieldOTP: FC<{ ref: [] as HTMLInputElement[], }); + // useEffect(() => { + // if (typeof fm.data[field.name] === "string") { + // local.otp = fm.data[field.name].split(""); + // local.render(); + // } + // }, [fm.data[field.name]]); + if (local.otp.length === 0 && digit) { for (let i = 0; i < digit; i++) { local.otp.push(""); @@ -74,10 +81,8 @@ export const FieldOTP: FC<{ } const otp = local.otp.join(""); - if (otp.length === digit) { - fm.data[field.name] = otp; - fm.render(); - } + fm.data[field.name] = otp; + fm.render(); local.render(); }} /> diff --git a/preset/login/utils/register.ts b/preset/login/utils/register.ts index be69f9c..7ba92bb 100755 --- a/preset/login/utils/register.ts +++ b/preset/login/utils/register.ts @@ -1,3 +1,4 @@ +import { EsensiSession } from "app/server/session"; import day from "dayjs"; const w = window as any; @@ -8,7 +9,7 @@ export type RG = { }; export type UserSession = { - data: any; + data: EsensiSession; expired: Date; // second }; export const registerSession = (session: RG) => { diff --git a/preset/menu/Layout.tsx b/preset/menu/Layout.tsx index d639f24..c1ea0eb 100755 --- a/preset/menu/Layout.tsx +++ b/preset/menu/Layout.tsx @@ -7,8 +7,9 @@ import { FieldLoading } from "lib/comps/ui/field-loading"; const w = window as any; const initResponsive = function () { - const mode = localStorage.getItem("prasi-editor-mode"); + let mode = "desktop"; if (isEditor) { + const mode = localStorage.getItem("prasi-editor-mode"); setTimeout(() => { if (mode === "mobile") { w.isMobile = true; diff --git a/server/server-route.ts b/server/server-route.ts index 00316fb..0858384 100755 --- a/server/server-route.ts +++ b/server/server-route.ts @@ -4,8 +4,8 @@ import { ServerContext, SessionContext } from "../session/type"; type RouteFn = (...arg: any[]) => Promise; -type SingleRoute = [string, () => Promise<{ default: RouteFn }>]; -type SingleRouteWithOption = [ +export type SingleRoute = [string, () => Promise<{ default: RouteFn }>]; +export type SingleRouteWithOption = [ string, () => Promise<{ default: RouteFn }>, RouteOption @@ -22,6 +22,10 @@ export const newServerRouter = < return arg; }; +export const prasiApi = any>(fn: T) => { + return fn as (...arg: Parameters) => ReturnType; +}; + export const newClientRouter = >( ...routers: T[] ) => { @@ -33,6 +37,7 @@ export const newClientRouter = >( for (const router of routers) { if (router[api_name as any]) { const [url, _, opt] = router[api_name as any]; + if (opt && opt.response_as) return _post(url, args, { response_as: opt.response_as }); @@ -84,7 +89,11 @@ export const useServerRouter = >( try { params = await req.json(); } catch (e) {} - result = await route.handler.default.bind(arg)(params); + if (Array.isArray(params)) { + result = await route.handler.default.bind(arg)(...params); + } else { + result = await route.handler.default.bind(arg)(params); + } } if (typeof result === "object" && result instanceof Response) { diff --git a/session/client-session.ts b/session/client-session.ts new file mode 100755 index 0000000..ed05fae --- /dev/null +++ b/session/client-session.ts @@ -0,0 +1,104 @@ +import { LoginResult } from "app/server/router/login"; +import { + newClientRouter, + SingleRoute, + SingleRouteWithOption, +} from "../server/server-route"; +import { ClientSession, SessionAuth } from "./type"; + +export const newClientSession = (arg: { + tracker?: { enabled?: boolean }; + route: { + login: SingleRoute | SingleRouteWithOption; + }; + on: { + save: (data: T) => Promise; + load: () => Promise; + clear: () => Promise; + messageReceived?: (session: ClientSession) => Promise; + afterLogin: (arg: LoginResult, session: ClientSession) => Promise; + afterLogout?: (session: ClientSession) => Promise; + afterInit: (session: ClientSession) => Promise; + }; +}) => { + const router = { login: arg.route.login }; + const client = newClientRouter(router); + + const login_promise = { resolve: null as any, reject: null as any }; + const logout_promise = { resolve: null as any, reject: null as any }; + + const session: ClientSession = { + status: "checking", + current: null, + connected: false, + save: arg.on.save, + load: arg.on.load, + clear: arg.on.clear, + async init() { + arg.on.afterInit(session); + + return { status: this.status }; + }, + login(auth: SessionAuth) { + return new Promise(async (resolve, reject) => { + if (isEditor) { + resolve({} as any); + return; + } + + login_promise.resolve = resolve; + login_promise.reject = reject; + if (this.status === "checking") { + await new Promise((done) => { + const ival = setInterval(() => { + if (this.status !== "checking") { + clearInterval(ival); + done(); + } + }, 100); + }); + } + + if (this.status !== "active") { + const result: LoginResult = await client.login(auth); + arg.on.afterLogin(result, session); + } else { + console.error( + `\ +Session login failed, current status is: ${this.status}. +Login is prevented, please logout first before re-login!` + ); + } + + if (this.current) { + resolve(this.current); + } else { + if (auth) { + } else { + reject("Current session not found"); + } + } + }); + }, + logout() { + return new Promise(async (resolve, reject) => { + if (isEditor) { + resolve({} as any); + return; + } + + logout_promise.resolve = resolve; + logout_promise.reject = reject; + + if (arg.on.afterLogout) { + arg.on.afterLogout(session); + } + }); + }, + }; + + if (!isEditor) { + session.init(); + } + return session; +}; diff --git a/session/server-session.ts b/session/server-session.ts new file mode 100755 index 0000000..61638de --- /dev/null +++ b/session/server-session.ts @@ -0,0 +1,56 @@ +/// + +import { ServerWebSocket } from "bun"; +import { useServerRouter } from "../server/server-route"; +import { newSessionStore } from "./store/session-store"; +import { ServerContext } from "./type"; + +type WS = ServerWebSocket<{ url: string }>; +type SessionServerHandler = { + cleanup: () => Promise; + handle: (arg: ServerContext) => Promise; +}; + +export const initSessionServer = ( + server: PrasiServer, + arg: { + encrypt?: boolean; + router: ReturnType; + } +) => { + try { + const session_store = newSessionStore(server.site_id); + const session_router = arg.router; + const server_handler: SessionServerHandler = { + async cleanup() {}, + + async handle(server_arg) { + const { req, handle, url } = server_arg; + + const route_arg = { + ...server_arg, + session: { + ...session_store, + current: undefined, + }, + }; + + if (url.pathname.startsWith("/_session/")) { + const res = await session_router.handle(route_arg); + if (res) return res; + } + + if (arg.router) { + const res = await arg.router.handle(route_arg); + if (res) return res; + } + + return handle(req); + }, + }; + + server.session = server_handler; + } catch (e) { + console.log(e); + } +}; diff --git a/session/type.ts b/session/type.ts index 8d9d6a0..7276244 100755 --- a/session/type.ts +++ b/session/type.ts @@ -1,3 +1,5 @@ +import { Server } from "bun"; + export interface NewSessionData { uid: string; role: string; @@ -52,12 +54,14 @@ export type ClientSessionStatus = | "logout"; export type ClientSession = { status: ClientSessionStatus; - current: null | SessionData; + current: null | T; init(): Promise<{ status: ClientSessionStatus }>; - connect(auth?: SessionAuth): Promise; connected: boolean; - login(auth: SessionAuth): Promise>; + login(auth: SessionAuth): Promise; logout(): Promise; + save(data: T): Promise; + load(): Promise; + clear(): Promise; }; export interface SessionContext extends ServerContext { @@ -66,6 +70,7 @@ export interface SessionContext extends ServerContext { export type ServerContext = { req: Request; + server: Server; handle: (req: Request) => Promise; mode: "dev" | "prod"; url: {