diff --git a/modules.json b/modules.json index 3f5ace2..2e6f659 100755 --- a/modules.json +++ b/modules.json @@ -23,6 +23,7 @@ "@types/react": "^18.2.65", "bun-types": "^1.1.24", "@types/react-dom": "^18.3.0", + "rou3": "^0.5.1", "@types/uuid": "^9.0.8", "@wojtekmaj/react-qr-svg": "^1.0.0", "any-date-parser": "^1.5.4", diff --git a/server/server-route.ts b/server/server-route.ts index 546dedd..dd8f265 100755 --- a/server/server-route.ts +++ b/server/server-route.ts @@ -1,3 +1,6 @@ +import { _post } from "lib/utils/post"; +import { addRoute, createRouter, findRoute } from "rou3"; + export type ServerArg = { req: Request; handle: (req: Request) => Promise; @@ -7,7 +10,85 @@ export type ServerArg = { pathname: string; }; }; -export const newRoute = (arg: { - url: string; - handler: (arg: ServerArg) => Promise | Response | void; -}) => {}; + +type RouteFn = (...arg: any[]) => Promise; + +type SingleRoute = [string, () => Promise<{ default: RouteFn }>]; +type SingleRouteWithOption = [ + string, + () => Promise<{ default: RouteFn }>, + RouteOption +]; +type RouteOption = { + request_as?: "json" | "raw"; + response_as?: "json" | "raw"; +}; +export const newRouter = < + T extends Record +>( + arg: T +) => { + return arg; +}; + +export const serverRouting = >( + router: T +) => { + return new Proxy( + {}, + { + get(target, api_name, receiver) { + return (...args: any[]) => { + const [url, _, opt] = router[api_name as any]; + if (opt && opt.response_as) + return _post(url, args, { response_as: opt.response_as }); + + return _post(url, args); + }; + }, + } + ) as { + [K in keyof T]: Awaited>["default"]; + }; +}; + +export const useServerRoutes = async >( + router: T +) => { + const rou = createRouter<{ + handler: { default: RouteFn }; + opt?: RouteOption; + }>(); + + for (const item of Object.values(router)) { + try { + addRoute(rou, undefined, item[0], { + handler: await (item as any)[1](), + opt: item[2], + }); + } catch (e) {} + } + + return { + async handle(arg: ServerArg) { + const { url, req } = arg; + const found = findRoute(rou, undefined, url.pathname); + + if (found) { + const route = found.data; + + let result = null; + if (!route.opt || route.opt?.request_as === "raw") { + result = await route.handler.default(); + } else { + const params = await req.json(); + result = await route.handler.default(...params); + } + + if (result) return result; + else return new Response(JSON.stringify(result)); + } + return await arg.handle(arg.req); + }, + }; +}; diff --git a/server/server-session.ts b/server/server-session.ts index abc3f29..4d99a07 100755 --- a/server/server-session.ts +++ b/server/server-session.ts @@ -1,6 +1,6 @@ /// -import { ServerArg } from "./server-route"; +import { ServerArg, useServerRoutes } from "./server-route"; type ServerSession = { handle: (arg: ServerArg) => Promise; @@ -8,9 +8,7 @@ type ServerSession = { export const sessionServer = (arg: { encrypt?: boolean; - router?: ( - arg: ServerArg & { session: {} } - ) => Response | (() => Promise) | void; + router?: ReturnType; on: { login: (arg: { mode: "user-pass"; @@ -19,23 +17,31 @@ export const sessionServer = (arg: { }) => Promise; }; }): ServerSession => { + const internal = { + has_router: false, + router: null as null | Awaited>, + }; + if (typeof arg.router === "object" && arg.router instanceof Promise) { + internal.has_router = true; + arg.router.then((e) => { + internal.router = e; + }); + } const s: ServerSession = { async handle(server_arg) { - const { req, handle, mode, url } = server_arg; - if (typeof arg.router === "function") { - let result = arg.router({ - ...server_arg, - session: {}, - }); - if (result && typeof result === "function") { - result = await result(); - } - if (result instanceof Response) { + const { req, handle } = server_arg; + + if (internal.has_router && internal.router) { + const result = await internal.router.handle(server_arg); + + if (typeof result === "object" && result instanceof Response) { return result; } + + return new Response(JSON.stringify(result)); } - return await handle(req); + return handle(req); }, }; diff --git a/utils/post.ts b/utils/post.ts index a165cef..816ed9a 100755 --- a/utils/post.ts +++ b/utils/post.ts @@ -1,7 +1,9 @@ +import { baseurl } from "./baseurl"; + export const _post = async ( path: string, data: any, - arg?: { mode: "auto" | "always-prod" } + opt?: { server_target?: "auto" | "always-prod"; response_as?: "json" | "raw" } ) => { const final_path = path.startsWith("/") ? path : `/${path}`; @@ -11,7 +13,7 @@ export const _post = async ( location.hostname === "prasi.avolut.com" || location.host === "localhost:4550" ) { - if (arg?.mode === "always-prod") { + if (opt?.server_target === "always-prod") { const newurl = new URL(location.href); newurl.pathname = `/_proxy/${_url}`; _url = newurl.toString(); @@ -22,5 +24,27 @@ export const _post = async ( method: "POST", body: JSON.stringify(data), }); - return await res.json(); + let text = await res.text(); + if (!opt || (opt && opt.response_as === "json")) { + try { + return JSON.parse(text); + } catch (e) { + console.error(`\ +🚧 Server Error: ${path} +`); + console.error( + `%c⬆ DATA SENT: `, + "color:green", + `\n` + JSON.stringify(data, null, 2) + ); + console.error( + `%c⬇ DATA RECEIVED: `, + `color:red`, + `\n` + JSON.stringify(text, null, 2) + ); + console.error(`Failed to parse received data as JSON!`); + } + } else { + return text; + } };