From 6ddbd121cb11afd7b47b0a80e1794d48fb324ff8 Mon Sep 17 00:00:00 2001 From: Rizky Date: Sat, 16 Dec 2023 17:06:31 +0700 Subject: [PATCH] wip check point --- app/srv/api/auth/login.ts | 2 +- app/srv/api/auth/logout.ts | 2 +- app/srv/api/auth/session.ts | 2 +- app/srv/api/code.ts | 2 +- app/srv/api/comp.ts | 2 +- app/srv/api/font.ts | 2 +- app/srv/api/img.ts | 2 +- app/srv/api/local-ip.ts | 2 +- app/srv/api/nova-load.ts | 2 +- app/srv/api/npm-bundle.ts | 2 +- app/srv/api/npm-size.ts | 2 +- app/srv/api/npm.ts | 2 +- app/srv/api/page.ts | 2 +- app/srv/api/session.ts | 2 +- app/srv/api/site-bundle.ts | 2 +- app/srv/api/site-dts.ts | 2 +- app/srv/api/site-export.ts | 2 +- app/web/src/base/page/ed.tsx | 13 ++ app/web/src/index.tsx | 183 +---------------- app/web/src/nova/ed/logic/ed-init.ts | 1 + .../nova/ed/panel/popup/script/snippet.tsx | 1 - app/web/src/sworker-boot.tsx | 177 ++++++++++++++++ app/web/src/utils/types/general.ts | 1 + pkgs/core/api/_api_frm.ts | 2 +- pkgs/core/api/_dbs.ts | 2 +- pkgs/core/api/_file.ts | 2 +- pkgs/core/api/_prasi.ts | 2 +- pkgs/core/api/_upload.ts | 2 +- pkgs/core/index.ts | 7 +- pkgs/core/server/api-frm.ts | 70 ------- pkgs/core/server/{ => api}/api-ctx.ts | 0 pkgs/core/server/{ => api}/api-scan.ts | 4 +- pkgs/core/server/{ => api}/parse-args.ts | 0 pkgs/core/server/{ => api}/prep-api-ts.ts | 6 +- pkgs/core/server/create.ts | 141 ++----------- pkgs/core/server/serve-api.ts | 191 ++++++++++-------- pkgs/core/server/serve-static.ts | 59 ++++++ pkgs/core/server/serve-ws.ts | 41 ++++ tsconfig.json | 2 +- 39 files changed, 443 insertions(+), 498 deletions(-) create mode 100644 app/web/src/sworker-boot.tsx delete mode 100644 pkgs/core/server/api-frm.ts rename pkgs/core/server/{ => api}/api-ctx.ts (100%) rename pkgs/core/server/{ => api}/api-scan.ts (94%) rename pkgs/core/server/{ => api}/parse-args.ts (100%) rename pkgs/core/server/{ => api}/prep-api-ts.ts (93%) create mode 100644 pkgs/core/server/serve-static.ts create mode 100644 pkgs/core/server/serve-ws.ts diff --git a/app/srv/api/auth/login.ts b/app/srv/api/auth/login.ts index 0f0d909b..f674ee10 100644 --- a/app/srv/api/auth/login.ts +++ b/app/srv/api/auth/login.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../../pkgs/core/server/api/api-ctx"; import argon from "@node-rs/argon2"; import { session } from "utils/session"; diff --git a/app/srv/api/auth/logout.ts b/app/srv/api/auth/logout.ts index 42414062..57d35f9d 100644 --- a/app/srv/api/auth/logout.ts +++ b/app/srv/api/auth/logout.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../../pkgs/core/server/api/api-ctx"; import { session } from "utils/session"; export const _ = { diff --git a/app/srv/api/auth/session.ts b/app/srv/api/auth/session.ts index 55da5aaf..90531dc0 100644 --- a/app/srv/api/auth/session.ts +++ b/app/srv/api/auth/session.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../../pkgs/core/server/api/api-ctx"; import { user } from "dbgen"; import { session } from "utils/session"; diff --git a/app/srv/api/code.ts b/app/srv/api/code.ts index 93757328..a30f68f1 100644 --- a/app/srv/api/code.ts +++ b/app/srv/api/code.ts @@ -1,7 +1,7 @@ import { dirAsync } from "fs-jetpack"; import trim from "lodash.trim"; import { dirname } from "path"; -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { g } from "utils/global"; import { baseTypings } from "../../web/src/utils/script/types/base"; diff --git a/app/srv/api/comp.ts b/app/srv/api/comp.ts index 40c46905..89a7e0e3 100644 --- a/app/srv/api/comp.ts +++ b/app/srv/api/comp.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; export const _ = { url: "/_web/comp/:id", diff --git a/app/srv/api/font.ts b/app/srv/api/font.ts index f3958d04..0545f84d 100644 --- a/app/srv/api/font.ts +++ b/app/srv/api/font.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; export const _ = { url: "/_font/**", diff --git a/app/srv/api/img.ts b/app/srv/api/img.ts index cbcef0f1..44d9cd2c 100644 --- a/app/srv/api/img.ts +++ b/app/srv/api/img.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { g } from "utils/global"; export const _ = { diff --git a/app/srv/api/local-ip.ts b/app/srv/api/local-ip.ts index 8baf9dc5..86057efd 100644 --- a/app/srv/api/local-ip.ts +++ b/app/srv/api/local-ip.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; export const _ = { url: "/local-ip", async api() { diff --git a/app/srv/api/nova-load.ts b/app/srv/api/nova-load.ts index 6a0a4b6b..47244afc 100644 --- a/app/srv/api/nova-load.ts +++ b/app/srv/api/nova-load.ts @@ -1,5 +1,5 @@ import { dir } from "dir"; -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { g } from "utils/global"; export const _ = { diff --git a/app/srv/api/npm-bundle.ts b/app/srv/api/npm-bundle.ts index c1f91968..cfa350a5 100644 --- a/app/srv/api/npm-bundle.ts +++ b/app/srv/api/npm-bundle.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { buildNpm } from "../util/build-npm"; export const _ = { diff --git a/app/srv/api/npm-size.ts b/app/srv/api/npm-size.ts index 1b52b704..b338a543 100644 --- a/app/srv/api/npm-size.ts +++ b/app/srv/api/npm-size.ts @@ -1,6 +1,6 @@ import { dir } from "dir"; import { stat } from "fs/promises"; -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { g } from "utils/global"; export const _ = { diff --git a/app/srv/api/npm.ts b/app/srv/api/npm.ts index 9872d8b1..4db678d0 100644 --- a/app/srv/api/npm.ts +++ b/app/srv/api/npm.ts @@ -2,7 +2,7 @@ import crypto from "crypto"; import { dir } from "dir"; import { readAsync } from "fs-jetpack"; import mime from "mime-types"; -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { glb } from "../global"; import { g } from "utils/global"; diff --git a/app/srv/api/page.ts b/app/srv/api/page.ts index 5530d27d..2e790c2c 100644 --- a/app/srv/api/page.ts +++ b/app/srv/api/page.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; export const _ = { url: "/_web/page/:id", diff --git a/app/srv/api/session.ts b/app/srv/api/session.ts index 9920af19..b4423f49 100644 --- a/app/srv/api/session.ts +++ b/app/srv/api/session.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { session } from "utils/session"; import { user } from "dbgen"; diff --git a/app/srv/api/site-bundle.ts b/app/srv/api/site-bundle.ts index 91c302dc..6baa23d8 100644 --- a/app/srv/api/site-bundle.ts +++ b/app/srv/api/site-bundle.ts @@ -1,7 +1,7 @@ import { createHash } from "crypto"; import { dir } from "dir"; import { readAsync } from "fs-jetpack"; -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; const cache = { md5: "", content: null as any, diff --git a/app/srv/api/site-dts.ts b/app/srv/api/site-dts.ts index 1ded005d..29e832fc 100644 --- a/app/srv/api/site-dts.ts +++ b/app/srv/api/site-dts.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import ts from "typescript"; import { createHash } from "crypto"; diff --git a/app/srv/api/site-export.ts b/app/srv/api/site-export.ts index f46e8798..05ee1399 100644 --- a/app/srv/api/site-export.ts +++ b/app/srv/api/site-export.ts @@ -1,4 +1,4 @@ -import { apiContext } from "service-srv"; +import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { dir } from "dir"; import fs from "fs"; diff --git a/app/web/src/base/page/ed.tsx b/app/web/src/base/page/ed.tsx index dd2719d7..1ff436b8 100644 --- a/app/web/src/base/page/ed.tsx +++ b/app/web/src/base/page/ed.tsx @@ -7,6 +7,19 @@ import { Loading } from "../../utils/ui/loading"; export default page({ url: "/ed/:site_id/:page_id", component: ({}) => { + console.log("momoka"); + setTimeout(() => { + (async () => { + console.log( + await fetch("/moka", { + method: "POST", + body: "{}", + }) + ); + })(); + }, 2000); + return <>uwuw; + const p = useGlobal(EDGlobal, "EDITOR"); const w = window as any; diff --git a/app/web/src/index.tsx b/app/web/src/index.tsx index 0390a7b7..86fa3440 100644 --- a/app/web/src/index.tsx +++ b/app/web/src/index.tsx @@ -2,197 +2,30 @@ import { Root as ReactRoot, createRoot } from "react-dom/client"; import { defineReact, defineWindow } from "web-utils"; import { Root } from "./base/root"; import "./index.css"; -import { createAPI, createDB, reloadDBAPI } from "./utils/script/init-api"; -import { w } from "./utils/types/general"; import { registerMobile } from "./render/live/logic/mobile"; -import { isLocalhost } from "./utils/ui/is-localhost"; +import { reloadDBAPI } from "./utils/script/init-api"; +import { w } from "./utils/types/general"; +import { sworkerAddCache, sworkerRegister } from "./sworker-boot"; const start = async () => { const base = `${location.protocol}//${location.host}`; let react = { root: null as null | ReactRoot, }; - (window as any).mobile = registerMobile(); - - if (navigator.serviceWorker) { - if (!isLocalhost()) { - const sw = await registerServiceWorker(); - const cacheCurrentPage = () => { - const swController = navigator.serviceWorker.controller; - if (swController) { - [location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach( - (url) => { - swController.postMessage({ - type: "add-cache", - url: url, - }); - } - ); - } - }; - cacheCurrentPage(); - navigator.serviceWorker.addEventListener("message", (e) => { - cacheCurrentPage(); - if (react.root) { - if (e.data.type === "offline") { - w.offline = true; - const click = () => { - if (react.root) react.root.render(); - }; - setTimeout(click, 5000); - react.root.render( - <> - -
-
- Network Failed -
-
- - ); - } - - if (e.data.type === "activated") { - if (e.data.shouldRefresh && sw) { - react.root.render( - <> - -
-
- Updating App... -
-
- - ); - - sw.unregister().then(() => { - window.location.reload(); - }); - } else { - const localVersion = localStorage.getItem("prasi-version"); - if (localVersion !== e.data.version) { - localStorage.setItem("prasi-version", e.data.version); - const click = () => { - if (react.root) react.root.render(); - }; - setTimeout(click, 5000); - react.root.render( - <> - -
-
- Prasi Updated{" "} - {e.data.version} -
-
- - ); - } - } - } - } - }); - } else { - navigator.serviceWorker.getRegistrations().then(function (registrations) { - for (let registration of registrations) { - registration.unregister(); - } - }); - } - } + w.mobile = registerMobile(); + // sworkerRegister(react); defineReact(); await defineWindow(false); - w.serverurl = base; - await reloadDBAPI(base, "prod"); - - if (navigator.serviceWorker) { - const swc = navigator.serviceWorker.controller; - if (swc) { - [location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach((url) => { - swc.postMessage({ - type: "add-cache", - url: url, - }); - }); - if (w.prasiApi && w.prasiApi[base] && w.prasiApi[base].apiEntry) { - const routes = Object.entries(w.prasiApi[base].apiEntry).map( - ([k, v]: any) => ({ - url: v.url, - name: k, - }) - ); - - swc.postMessage({ - type: "define-route", - routes, - }); - } - } - } - w.api = createAPI(base); - w.db = createDB(base); + // await reloadDBAPI(base, "prod"); + // sworkerAddCache(base); const el = document.getElementById("root"); + if (el) { react.root = createRoot(el); react.root.render(); } }; -const registerServiceWorker = async () => { - if ("serviceWorker" in navigator) { - try { - return await navigator.serviceWorker.register( - new URL("./sworker.ts", import.meta.url), - { - type: "module", - scope: "/", - } - ); - } catch (error) { - console.error(`Registration failed with ${error}`); - } - } -}; - start(); diff --git a/app/web/src/nova/ed/logic/ed-init.ts b/app/web/src/nova/ed/logic/ed-init.ts index cf458806..f39c2c37 100644 --- a/app/web/src/nova/ed/logic/ed-init.ts +++ b/app/web/src/nova/ed/logic/ed-init.ts @@ -2,6 +2,7 @@ import init from "wasm-gzip"; import { jscript } from "../../../utils/script/jscript"; import { dbClient } from "../../vi/load/db/client-db"; import { PG } from "./ed-global"; +import { fetchViaProxy } from "../../vi/load/proxy"; let w = window as unknown as { db: ReturnType }; diff --git a/app/web/src/nova/ed/panel/popup/script/snippet.tsx b/app/web/src/nova/ed/panel/popup/script/snippet.tsx index d0209f3c..6b502a49 100644 --- a/app/web/src/nova/ed/panel/popup/script/snippet.tsx +++ b/app/web/src/nova/ed/panel/popup/script/snippet.tsx @@ -16,7 +16,6 @@ export const EdScriptSnippet: FC<{}> = ({}) => { font-size: 12px; `)} onClick={() => { - console.log(p.script.do_edit); p.script.do_edit( `\
diff --git a/app/web/src/sworker-boot.tsx b/app/web/src/sworker-boot.tsx new file mode 100644 index 00000000..f35c5eec --- /dev/null +++ b/app/web/src/sworker-boot.tsx @@ -0,0 +1,177 @@ +import { Root as ReactRoot } from "react-dom/client"; +import { Root } from "./base/root"; +import { w } from "./utils/types/general"; +import { isLocalhost } from "./utils/ui/is-localhost"; + +export const sworkerRegister = async (react: { root: null | ReactRoot }) => { + if (navigator.serviceWorker) { + if (!isLocalhost()) { + const sw = await registerServiceWorker(); + const cacheCurrentPage = () => { + const swController = navigator.serviceWorker.controller; + if (swController) { + [location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach( + (url) => { + swController.postMessage({ + type: "add-cache", + url: url, + }); + } + ); + } + }; + cacheCurrentPage(); + navigator.serviceWorker.addEventListener("message", (e) => { + cacheCurrentPage(); + if (react.root) { + if (e.data.type === "offline") { + w.offline = true; + const click = () => { + if (react.root) react.root.render(); + }; + setTimeout(click, 5000); + react.root.render( + <> + +
+
+ Network Failed +
+
+ + ); + } + + if (e.data.type === "activated") { + if (e.data.shouldRefresh && sw) { + react.root.render( + <> + +
+
+ Updating App... +
+
+ + ); + + sw.unregister().then(() => { + window.location.reload(); + }); + } else { + const localVersion = localStorage.getItem("prasi-version"); + if (localVersion !== e.data.version) { + localStorage.setItem("prasi-version", e.data.version); + const click = () => { + if (react.root) react.root.render(); + }; + setTimeout(click, 5000); + react.root.render( + <> + +
+
+ Prasi Updated{" "} + {e.data.version} +
+
+ + ); + } + } + } + } + }); + } else { + navigator.serviceWorker.getRegistrations().then(function (registrations) { + for (let registration of registrations) { + registration.unregister(); + } + }); + } + } +}; + +const registerServiceWorker = async () => { + if ("serviceWorker" in navigator) { + try { + return await navigator.serviceWorker.register( + new URL("./sworker.ts", import.meta.url), + { + type: "module", + scope: "/", + } + ); + } catch (error) { + console.error(`Registration failed with ${error}`); + } + } +}; + +export const sworkerAddCache = (base: string) => { + if (navigator.serviceWorker) { + if (!isLocalhost()) { + const swc = navigator.serviceWorker.controller; + if (swc) { + [location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach((url) => { + swc.postMessage({ + type: "add-cache", + url: url, + }); + }); + if (w.prasiApi && w.prasiApi[base] && w.prasiApi[base].apiEntry) { + const routes = Object.entries(w.prasiApi[base].apiEntry).map( + ([k, v]: any) => ({ + url: v.url, + name: k, + }) + ); + + swc.postMessage({ + type: "define-route", + routes, + }); + } + } + } + } +}; diff --git a/app/web/src/utils/types/general.ts b/app/web/src/utils/types/general.ts index 9e08fe45..d47ea983 100644 --- a/app/web/src/utils/types/general.ts +++ b/app/web/src/utils/types/general.ts @@ -27,6 +27,7 @@ export const w = window as unknown as { prasiApi: Record; loadedFonts: string[]; prasiApiDbPull: boolean; + mobile?: any; params: any; editorGlbDefault: string; ts: number; diff --git a/pkgs/core/api/_api_frm.ts b/pkgs/core/api/_api_frm.ts index a6d29abf..3ddb7541 100644 --- a/pkgs/core/api/_api_frm.ts +++ b/pkgs/core/api/_api_frm.ts @@ -1,4 +1,4 @@ -import { apiContext } from "../server/api-ctx"; +import { apiContext } from "../server/api/api-ctx"; import { g } from "../utils/global"; export const _ = { diff --git a/pkgs/core/api/_dbs.ts b/pkgs/core/api/_dbs.ts index 041a0e05..8fd955b9 100644 --- a/pkgs/core/api/_dbs.ts +++ b/pkgs/core/api/_dbs.ts @@ -1,4 +1,4 @@ -import { apiContext } from "../server/api-ctx"; +import { apiContext } from "../server/api/api-ctx"; import { DBArg, execQuery } from "../utils/query"; export const _ = { diff --git a/pkgs/core/api/_file.ts b/pkgs/core/api/_file.ts index e96b8d2b..99bd2d0c 100644 --- a/pkgs/core/api/_file.ts +++ b/pkgs/core/api/_file.ts @@ -1,5 +1,5 @@ import { g } from "utils/global"; -import { apiContext } from "../server/api-ctx"; +import { apiContext } from "../server/api/api-ctx"; import { dir } from "../utils/dir"; export const _ = { diff --git a/pkgs/core/api/_prasi.ts b/pkgs/core/api/_prasi.ts index a3eeba3c..ce371c3a 100644 --- a/pkgs/core/api/_prasi.ts +++ b/pkgs/core/api/_prasi.ts @@ -1,5 +1,5 @@ import { readAsync } from "fs-jetpack"; -import { apiContext } from "../server/api-ctx"; +import { apiContext } from "../server/api/api-ctx"; import { g } from "../utils/global"; import { dir } from "../utils/dir"; diff --git a/pkgs/core/api/_upload.ts b/pkgs/core/api/_upload.ts index 63f6eef0..d6d8857a 100644 --- a/pkgs/core/api/_upload.ts +++ b/pkgs/core/api/_upload.ts @@ -2,7 +2,7 @@ import mp from "@surfy/multipart-parser"; import { writeAsync } from "fs-jetpack"; import { g } from "utils/global"; -import { apiContext } from "../server/api-ctx"; +import { apiContext } from "../server/api/api-ctx"; import { dir } from "../utils/dir"; export const _ = { url: "/_upload", diff --git a/pkgs/core/index.ts b/pkgs/core/index.ts index 01f57aad..a5ee74ab 100644 --- a/pkgs/core/index.ts +++ b/pkgs/core/index.ts @@ -1,7 +1,5 @@ import { parcelBuild } from "utils/parcel"; -import { generateAPIFrm } from "./server/api-frm"; -import { prepareApiRoutes } from "./server/api-scan"; -import { prepareAPITypes } from "./server/prep-api-ts"; +import { prepareAPITypes } from "./server/api/prep-api-ts"; import { startDevWatcher } from "./utils/dev-watcher"; import { ensureNotRunning } from "./utils/ensure"; import { g } from "./utils/global"; @@ -55,14 +53,11 @@ if (!g.apiPrepared) { await syncActionDefinition(); g.log.info("WS Action defined"); - await generateAPIFrm(); await prepareAPITypes(); g.log.info("API Prepared"); g.apiPrepared = true; } -await prepareApiRoutes(); - if (!g.parcel) { await parcelBuild(); } diff --git a/pkgs/core/server/api-frm.ts b/pkgs/core/server/api-frm.ts deleted file mode 100644 index df941137..00000000 --- a/pkgs/core/server/api-frm.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { transform } from "@swc/core"; -import { g } from "../utils/global"; -import { createHash } from "crypto"; - -export const generateAPIFrm = async () => { - const res = await transform( - ` - (BigInt.prototype).toJSON = function () { - return "BigInt::" + this.toString(); - }; - - const replacer = (key, value) => { - if (typeof value === "string" && value.startsWith('BigInt::')) { - return BigInt(value.substr(8)); - } - return value; - } - - window.addEventListener('message', (e) => { - const msg = e.data; - const init = Object.assign({}, msg.init) - - let input = msg.input; - let url = msg.input; - if (typeof msg.input === 'string') { - if (!input.startsWith('http')) { - url = new URL(\`\$\{location.origin\}\$\{input\}\`) - } else { - url = new URL(input) - } - } - - if (init && init.body && typeof init.body === 'object') { - if (Array.isArray(init.body)) { - const body = new FormData(); - body.append("file", init.body[0]); - init.body = body; - } - } - - - fetch(url.pathname, init) - .then(async (res) => { - if (res) { - const body = await res.text(); - if (res.ok) { - try { - parent.postMessage({result: JSON.parse(body, replacer), id: msg.id }, '*') - } catch(e) { - parent.postMessage({result: body, id: msg.id }, '*') - } - } else { - try { - parent.postMessage({error: JSON.parse(body, replacer), id: msg.id }, '*') - } catch(e) { - parent.postMessage({error: body, id: msg.id }, '*') - } - } - } - }) - }) - parent.postMessage('initialized', '*')`, - { minify: true } - ); - - g.frm = { - js: res.code, - etag: createHash("md5").update(res.code).digest("hex"), - }; -}; diff --git a/pkgs/core/server/api-ctx.ts b/pkgs/core/server/api/api-ctx.ts similarity index 100% rename from pkgs/core/server/api-ctx.ts rename to pkgs/core/server/api/api-ctx.ts diff --git a/pkgs/core/server/api-scan.ts b/pkgs/core/server/api/api-scan.ts similarity index 94% rename from pkgs/core/server/api-scan.ts rename to pkgs/core/server/api/api-scan.ts index 8eacc046..f4893d12 100644 --- a/pkgs/core/server/api-scan.ts +++ b/pkgs/core/server/api/api-scan.ts @@ -1,8 +1,8 @@ import { file } from "bun"; import { inspectAsync, listAsync } from "fs-jetpack"; import { join } from "path"; -import { dir } from "../utils/dir"; -import { g } from "../utils/global"; +import { dir } from "../../utils/dir"; +import { g } from "../../utils/global"; import { parseArgs } from "./parse-args"; export const prepareApiRoutes = async () => { diff --git a/pkgs/core/server/parse-args.ts b/pkgs/core/server/api/parse-args.ts similarity index 100% rename from pkgs/core/server/parse-args.ts rename to pkgs/core/server/api/parse-args.ts diff --git a/pkgs/core/server/prep-api-ts.ts b/pkgs/core/server/api/prep-api-ts.ts similarity index 93% rename from pkgs/core/server/prep-api-ts.ts rename to pkgs/core/server/api/prep-api-ts.ts index 27b05fa1..5996c188 100644 --- a/pkgs/core/server/prep-api-ts.ts +++ b/pkgs/core/server/api/prep-api-ts.ts @@ -1,7 +1,7 @@ -import { spawn, spawnSync } from "bun"; +import { spawn } from "bun"; import { existsAsync, readAsync } from "fs-jetpack"; -import { dir } from "../utils/dir"; -import { g } from "../utils/global"; +import { dir } from "../../utils/dir"; +import { g } from "../../utils/global"; export const prepareAPITypes = async () => { const out: string[] = []; diff --git a/pkgs/core/server/create.ts b/pkgs/core/server/create.ts index eebdad58..df8a3572 100644 --- a/pkgs/core/server/create.ts +++ b/pkgs/core/server/create.ts @@ -1,8 +1,7 @@ -import { WebSocketHandler } from "bun"; -import { lookup } from "mime-types"; import { createRouter } from "radix3"; -import { dir } from "../utils/dir"; import { g } from "../utils/global"; +import { serveWS } from "./serve-ws"; +import { serveStatic } from "./serve-static"; import { serveAPI } from "./serve-api"; export const cache = { @@ -19,127 +18,24 @@ export const cache = { export type WSData = { url: URL }; export const createServer = async () => { - g.router = createRouter({ strictTrailingSlash: false }); + await serveAPI.init(); + await serveStatic.init(); - for (const route of Object.values(g.api)) { - g.router.insert(route.url.replace(/\*/gi, "**"), route); - } - - const { wsHandler } = await import("../../../app/srv/ws/handler"); g.server = Bun.serve({ port: g.port, - websocket: { - maxPayloadLength: 9999999, - closeOnBackpressureLimit: true, - drain(ws) { - // console.log("Backpressure relieved..."); - }, - close(ws, code, reason) { - const pathname = ws.data.url.pathname; - if (wsHandler[pathname]) { - const close = wsHandler[pathname].close; - if (close) { - close(ws, code, reason); - } - } - }, - message(ws, message) { - const pathname = ws.data.url.pathname; - if (wsHandler[pathname]) { - const msg = wsHandler[pathname].message; - if (msg) { - msg(ws, message); - } - } - }, - open(ws) { - const pathname = ws.data.url.pathname; - if (wsHandler[pathname]) { - const open = wsHandler[pathname].open; - if (open) { - open(ws); - } - } - }, - } as WebSocketHandler, + maxRequestBodySize: 9999999, + development: true, + websocket: await serveWS(), async fetch(req, server) { const url = new URL(req.url); - const response = async () => { - if (wsHandler[url.pathname]) { - if ( - server.upgrade(req, { - data: { - url: new URL(req.url), - }, - }) - ) { - return; - } - return new Response("Upgrade failed :(", { status: 500 }); - } + if (serveStatic.exists(url)) { + return serveStatic.serve(url); + } - try { - const api = await serveAPI(url, req); - if (api) { - return api; - } - } catch (e) { - g.log.error(e); - } + await serveAPI.serve(url, req); - const webPath = "app/static"; - try { - const found = cache.static[url.pathname]; - - if (found && g.mode === "prod") { - return responseCached(req, found); - } - - const file = Bun.file(dir.path(`${webPath}${url.pathname}`)); - if ( - (await file.exists()) && - file.type !== "application/octet-stream" // is not directory - ) { - if (g.mode === "dev") { - return new Response(file); - } - - if (!cache.static[url.pathname]) { - cache.static[url.pathname] = { - type: lookup(url.pathname) || "text/plain", - content: await file.arrayBuffer(), - }; - } - - const filebr = Bun.file(dir.path(`${webPath}-br${url.pathname}`)); - if ( - (await filebr.exists()) && - filebr.type !== "application/octet-stream" // is not directory - ) { - cache.static[url.pathname].br = await filebr.arrayBuffer(); - } - - const found = cache.static[url.pathname]; - if (found) { - return responseCached(req, found); - } - } - } catch (e) { - g.log.error(e); - } - - try { - return new Response( - Bun.file(dir.path(`${webPath}/index.html`)) as any - ); - } catch (e) { - g.log.error(e); - return new Response("Loading..."); - } - }; - const res = await response(); - return res; + return serveStatic.serve(url); }, }); @@ -149,16 +45,3 @@ export const createServer = async () => { g.log.info(`Started at port: ${g.server.port}`); } }; - -const responseCached = (req: Request, found: (typeof cache.static)[string]) => { - if (req.headers.get("accept-encoding")?.includes("br") && found.br) { - const res = new Response(found.br); - res.headers.set("content-type", found.type); - res.headers.set("content-encoding", "br"); - return res; - } - const res = new Response(found.content); - res.headers.set("content-type", found.type); - - return res; -}; diff --git a/pkgs/core/server/serve-api.ts b/pkgs/core/server/serve-api.ts index 73ec21fe..592eff7d 100644 --- a/pkgs/core/server/serve-api.ts +++ b/pkgs/core/server/serve-api.ts @@ -1,5 +1,105 @@ -import { createResponse } from "./api-ctx"; -import { g } from "../utils/global"; +import { createRouter } from "radix3"; +import { g } from "utils/global"; +import { createResponse } from "./api/api-ctx"; +import { prepareApiRoutes } from "./api/api-scan"; + +export const serveAPI = { + init: async () => { + g.router = createRouter({ strictTrailingSlash: false }); + for (const route of Object.values(g.api)) { + g.router.insert(route.url.replace(/\*/gi, "**"), route); + } + await prepareApiRoutes(); + }, + serve: async (url: URL, req: Request) => { + let found = g.router.lookup(url.pathname); + if (!found?.url) { + if (!url.pathname.endsWith("/")) { + found = g.router.lookup(url.pathname + "/"); + } + + if (!found?.url) { + found = null; + } + } + + if (found) { + const params = { ...found.params }; + + let args = found.args.map((e) => { + return params[e]; + }); + + if (req.method !== "GET") { + if ( + !req.headers.get("content-type")?.startsWith("multipart/form-data") + ) { + try { + const text = await req.text(); + const json = JSON.parse(text, replacer); + + if (typeof json === "object") { + if (Array.isArray(json)) { + args = json; + for (let i = 0; i < json.length; i++) { + const val = json[i]; + if (found.args[i]) { + params[found.args[i]] = val; + } + } + } else { + for (const [k, v] of Object.entries(json)) { + params[k] = v; + } + for (const [k, v] of Object.entries(params)) { + const idx = found.args.findIndex((arg) => arg === k); + if (idx >= 0) { + args[idx] = v; + } + } + } + } + } catch (e) { + throw e; + } + } + } + + const current = { + req, + res: new Response(), + ...found, + params, + }; + + const finalResponse = await current.fn(...args); + + if (finalResponse instanceof Response) { + return finalResponse; + } + + if (finalResponse) { + return createResponse(current.res, finalResponse); + } + if ( + (current.res as any)._status && + (current.res as any)._status !== current.res.status + ) { + const res = new Response(current.res.body, { + status: (current.res as any)._status, + }); + + current.res.headers.forEach((v, k) => { + res.headers.set(k, v); + }); + + return res; + } + + return current.res; + } + }, +}; const replacer = (key: string, value: string) => { if (typeof value === "string" && value.startsWith("BigInt::")) { @@ -7,90 +107,3 @@ const replacer = (key: string, value: string) => { } return value; }; - -export const serveAPI = async (url: URL, req: Request) => { - let found = g.router.lookup(url.pathname); - if (!found?.url) { - if (!url.pathname.endsWith("/")) { - found = g.router.lookup(url.pathname + "/"); - } - - if (!found?.url) { - found = null; - } - } - - if (found) { - const params = { ...found.params }; - - let args = found.args.map((e) => { - return params[e]; - }); - - if (req.method !== "GET") { - if (!req.headers.get("content-type")?.startsWith("multipart/form-data")) { - try { - const text = await req.text(); - const json = JSON.parse(text, replacer); - - if (typeof json === "object") { - if (Array.isArray(json)) { - args = json; - for (let i = 0; i < json.length; i++) { - const val = json[i]; - if (found.args[i]) { - params[found.args[i]] = val; - } - } - } else { - for (const [k, v] of Object.entries(json)) { - params[k] = v; - } - for (const [k, v] of Object.entries(params)) { - const idx = found.args.findIndex((arg) => arg === k); - if (idx >= 0) { - args[idx] = v; - } - } - } - } - } catch (e) { - g.log.error({ pathname: url.pathname, error: e }); - } - } - } - - const current = { - req, - res: new Response(), - ...found, - params, - }; - - const finalResponse = await current.fn(...args); - - if (finalResponse instanceof Response) { - return finalResponse; - } - - if (finalResponse) { - return createResponse(current.res, finalResponse); - } - if ( - (current.res as any)._status && - (current.res as any)._status !== current.res.status - ) { - const res = new Response(current.res.body, { - status: (current.res as any)._status, - }); - - current.res.headers.forEach((v, k) => { - res.headers.set(k, v); - }); - - return res; - } - - return current.res; - } -}; diff --git a/pkgs/core/server/serve-static.ts b/pkgs/core/server/serve-static.ts new file mode 100644 index 00000000..cad6fe8f --- /dev/null +++ b/pkgs/core/server/serve-static.ts @@ -0,0 +1,59 @@ +import { dir } from "dir"; +import { inspectTreeAsync } from "fs-jetpack"; +import { InspectTreeResult } from "fs-jetpack/types"; +import { join } from "path"; +import mime from "mime"; + +const webPath = "app/static"; +const cache = { + static: {} as Record, +}; + +export const serveStatic = { + init: async () => { + const list = await inspectTreeAsync(dir.path(`${webPath}`)); + const walk = async ( + list: InspectTreeResult, + parent?: InspectTreeResult[] + ) => { + for (const item of list.children) { + if (item.type === "file") { + const path = join( + ...(parent || [{ name: "static" }]).map((e) => e.name), + item.name + ); + const file = await Bun.file(dir.path(`app/${path}`)); + if (await file.exists()) { + cache.static[path.substring("static".length)] = { + type: mime.getType(path) || "application/octet-stream", + content: await file.arrayBuffer(), + }; + } + } else { + await walk(item, parent ? [...parent, list] : [list]); + } + } + }; + if (list) { + await walk(list); + } + }, + exists: (url: URL) => { + return !!cache.static[url.pathname]; + }, + serve: (url: URL) => { + const file = cache.static[url.pathname]; + if (file) { + return new Response(file.content, { + headers: { "content-type": file.type }, + }); + } + + const index = cache.static["/index.html"]; + if (index) { + return new Response(index.content, { + headers: { "content-type": index.type }, + }); + } + }, +}; diff --git a/pkgs/core/server/serve-ws.ts b/pkgs/core/server/serve-ws.ts new file mode 100644 index 00000000..2c9a3c7b --- /dev/null +++ b/pkgs/core/server/serve-ws.ts @@ -0,0 +1,41 @@ +import { WebSocketHandler } from "bun"; +import { WSData } from "./create"; + +export const serveWS: () => Promise> = async () => { + const { wsHandler } = await import("../../../app/srv/ws/handler"); + + return { + maxPayloadLength: 9999999, + closeOnBackpressureLimit: true, + drain(ws) { + // console.log("Backpressure relieved..."); + }, + close(ws, code, reason) { + const pathname = ws.data.url.pathname; + if (wsHandler[pathname]) { + const close = wsHandler[pathname].close; + if (close) { + close(ws, code, reason); + } + } + }, + message(ws, message) { + const pathname = ws.data.url.pathname; + if (wsHandler[pathname]) { + const msg = wsHandler[pathname].message; + if (msg) { + msg(ws, message); + } + } + }, + open(ws) { + const pathname = ws.data.url.pathname; + if (wsHandler[pathname]) { + const open = wsHandler[pathname].open; + if (open) { + open(ws); + } + } + }, + } as WebSocketHandler; +}; diff --git a/tsconfig.json b/tsconfig.json index bd06088a..414be545 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,7 +31,7 @@ "./node_modules/.prisma/client/index.d.ts" ], "service-srv": [ - "./pkgs/core/server/api-ctx.ts" + "./pkgs/core/server/api/api-ctx.ts" ], "utils/*": [ "./pkgs/core/utils/*"